first commit

main
翟帅帅 2025-04-16 20:15:33 +08:00
commit fd57c4cc62
87 changed files with 4413 additions and 0 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
study/eskf156/dataset/**/*.mat filter=lfs diff=lfs merge=lfs -text

55
.gitignore vendored Normal file
View File

@ -0,0 +1,55 @@
# Prerequisites
*.d
*.asv
*.bag
# *.csv
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf

38
README.md Normal file
View File

@ -0,0 +1,38 @@
# 基于超宽带(UWB)技术的扩展卡尔曼滤波(EKF)算法的 MATLAB 实现仓库
教学性质的:
* GPS IMU经典15维ESKF松组合
* VRU/AHRS姿态融合算法
* 捷联惯导速度位置姿态解算例子
* UWB IMU紧组合融合
* 每个例子自带数据集
运行环境:
* 最低版本: MATLAB R2022a, 必须安装sensor fusion toolbox和navigation tool box
* 需要将`\lib`及其子目录加入MATLAB预设目录 或者运行一下根目录下的`init.m`
其中UWB+IMU融合和GPS+IMU融合就是经典的15维误差卡尔曼滤波(EKSF),没有什么论文参考,就是一直用的经典的框架(就是松组合),见参考部分。
更多内容请访问:
- 知乎https://www.zhihu.com/people/yang-xi-97-90
- 网站www.hipnuc.com
参考:
1. 书:捷联惯导算法与组合导航原理 严恭敏及PSINS工具箱官方网站http://www.psins.org.cn/sy
2. 书GNSS与惯性及多传感器组合导航系统原理 第二版
3. 书GPS原理与接收机设计 谢刚
4. 深蓝学院-多传感器融合课程(理论推导及code)
5. 武汉大学牛小骥惯性导航课程(非常好,非常适合入门) https://www.bilibili.com/video/BV1nR4y1E7Yj
6. GPS IMU 松组合 https://kth.instructure.com/files/677996/download?download_frd=1
7. Coursera课程 https://www.coursera.org/learn/state-estimation-localization-self-driving-cars
推荐的学习路线:
1. 先看武汉大学惯性导航课程(牛小骥老师),入门非常推荐,也不需要什么教材,做笔记
2. 看严恭敏老师的书籍视频及code
3. 然后再入组合导航知识

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

25
UWBInsFusion/README.md Normal file
View File

@ -0,0 +1,25 @@
数据集说明:
所有数据集均为实测机器人跑车数据,无仿真数据。
| 数据集 | 基站数 | 平面or3D | 说明 |
| ------ | ------ | -------- | ------------------------------------------------------------ |
| datas1 | 4 | 2D | 机器人U型跑车数据机器人走走停停速度不稳定 |
| datas2 | 4 | 2D | 机器人圆形轨迹跑车数据,机器人匀速运动,运动形状简单规则,可以用来初步验证算法 |
| datas3 | 4 | 2D | 机器人走一个典型赛道轨迹, 基本匀速,比较典型的实测数据 |
| datas4 | 4 | 2D | 相较于datas3 运动更复杂,场地更大. 机动更剧烈 |
| datas5 | 4 | 2D | 类似datas4 |
| datas5 | 4 | 2D | 类似datas4 |
数据集采集指导:
如果您自己第一次采集数据做实验,建议:
1. 建议IMU 100Hz, UWB 1-10Hz左右
2. 用小车或者汽车,不要用手拿着,避免很多随机机动。
3. 开始静止至少30s 然后绕大8子每个8子绕完不少于30s。 可以走走停停。做完一个比较标准(好识别)的动作后 静止几秒再做下一个比较典型的有八字L型 绕圈等
4. 运动结束后最好也停止30s。

107
UWBInsFusion/demo_plot.m Normal file
View File

@ -0,0 +1,107 @@
%% 说明
function demo_plot(dataset, out_data)
figure('NumberTitle', 'off', 'Name', '原始数据');
subplot(2, 2, 1);
plot(dataset.imu.acc');
legend("X", "Y", "Z");
title("加速度测量值");
subplot(2, 2, 2);
plot(dataset.imu.gyr');
legend("X", "Y", "Z");
title("陀螺测量值");
subplot(2, 2, 3);
plot(dataset.uwb.tof');
title("UWB测量值(伪距)");
subplot(2,2,4);
plot(diff(dataset.uwb.tof'));
title("伪距的差分(diff(tof))");
figure('NumberTitle', 'off', 'Name', '滤波器估计零偏');
subplot(2,1,1);
plot(out_data.delta_u(:,1:3));
legend("X", "Y", "Z");
title("加速度零偏");
subplot(2,1,2);
plot(rad2deg(out_data.delta_u(:,4:6)));
legend("X", "Y", "Z");
title("陀螺仪零偏");
figure('NumberTitle', 'off', 'Name', '量测滤波信息');
subplot(2,1,1);
plot(out_data.L);
title("量测置信度S");
figure('NumberTitle', 'off', 'Name', 'PVT');
subplot(2,2,1);
plot(out_data.x(:,1:3));
legend("X", "Y", "Z");
title("位置");
subplot(2,2,2);
plot(out_data.x(:,4:6));
legend("X", "Y", "Z");
title("速度");
subplot(2,2,3);
plot(out_data.eul);
legend("X", "Y", "Z");
title("欧拉角(姿态)");
figure('NumberTitle', 'off', 'Name', '硬件给出的UWB解算位置');
subplot(1,2,1);
plot3(out_data.uwb.pos(1,:), out_data.uwb.pos(2,:), out_data.uwb.pos(3,:), '.');
axis equal
title("UWB 伪距解算位置");
if(isfield(dataset, "pos"))
subplot(1,2,2);
plot3(dataset.pos(1,:), dataset.pos(2,:), dataset.pos(3,:), '.');
hold on;
plot3(out_data.x(:,1), out_data.x(:,2), out_data.x(:,3), '.-');
axis equal
title("硬件给出轨迹");
end
figure('NumberTitle', 'off', 'Name', '纯UWB伪距解算的位置和融合轨迹');
plot(out_data.uwb.pos(1,:), out_data.uwb.pos(2,:), '.');
hold on;
plot(out_data.uwb.fusion_pos(1,:), out_data.uwb.fusion_pos(2,:), '.-');
anch = out_data.uwb.anchor;
hold all;
scatter(anch(1, :),anch(2, :),'k');
for i=1:size(anch,2)
text(anch(1, i),anch(2, i),"A"+(i-1))
end
hold off;
legend("伪距解算UWB轨迹", "融合轨迹");
%
% if(isfield(dataset, "pos"))
% figure('NumberTitle', 'off', 'Name', '硬件给出的位置和融合轨迹');
% plot(dataset.pos(1,:), dataset.pos(2,:), '.');
% hold on;
% plot(out_data.uwb.fusion_pos(1,:), out_data.uwb.fusion_pos(2,:), '.-');
% legend("硬件给出轨迹", "融合轨迹");
% anch = out_data.uwb.anchor;
% hold all;
% scatter(anch(1, :),anch(2, :),'k');
% for i=1:size(anch,2)
% text(anch(1, i),anch(2, i),"A"+(i-1))
% end
% hold off;
% end

355
UWBInsFusion/eskf_uwb_imu.m Normal file
View File

@ -0,0 +1,355 @@
clc
clear
close all
addpath './lib';
addpath './lib/gnss';
addpath './lib/calbiration';
addpath './lib/plot';
addpath './lib/rotation';
addpath './lib/geo';
savepath
%% 说明
% UWB IMU 融合算法采用误差卡尔曼15维经典模型伪距组合
% PR(TOF) 伪距: UWB硬件给出的原始测量距离值
% IMU: 加速度(3) 3轴陀螺(3) 共6维,,
% noimal_state: 名义状态: 导航方程状态: 位置(3) 速度(3) 四元数(4) 共10维
% err_state: KF误差状态: 位置误差(3) 速度误差(3) 失准角(3) 加速度计零偏(3) 陀螺零偏(3) 共15维
% du: 零偏反馈: 加速度计零偏(3) 陀螺零偏(3) 共6维
% 单位说明:
% 加速度,加速度零偏: m/s^(2)
% 角速度, 角速度(陀螺)零偏: rad/s
% 角度 rad
% 速度: m/s
%% 读取数据集
load datas2
dataset = datas;
% dataset的数据结构如下
% dataset
% ├── imu
% | ├── acc 加速度计三维数据
% | ├── gyr 陀螺仪三维数据
% | └── time 时间戳数组
% ├── uwb
% | ├── time 时间戳数组 1*17200
% | ├── anchor 四个标签的坐标矩阵xyz
% | ├── tof 飞行时间4*17200表示4个基站在连续时间点上对同一移动标签的测距时间
% | └── cnt 基站个数
% └── pos
N = length(dataset.imu.time); % 时间序列的长度
dt = mean(diff(dataset.imu.time)); % dtIMU数据的平均采样周期。通过diff方法计算相邻时间戳的差值并求平均值得到dt
% 故意删除一些基站及数据,看看算法在基站数量很少的时候能否有什么奇迹。。
% dataset.uwb.anchor(:,1) = [];
% dataset.uwb.tof(1,:) = [];
% EKF融合使用的基站个数融合算法最少2个基站就可以2D定位
%dataset.uwb.cnt = size(dataset.uwb.anchor, 2);
m_div_cntr = 0; % 量测分频器计数器用于记录已累计的IMU更新次数。当计数器达到 m_div 时触发UWB量测更新并重置计数器。
m_div = 1; % 量测分频系数每m_div次量测才更新一次EKF量测(UWB更新), 可以节约计算量 或者 做实验看效果
% UWB的采样率通常远低于IMU例如IMU 100HzUWB 10Hz直接每次融合会导致冗余计算。通过分频系数可以匹配两者的实际更新频率避免不必要的计算。
UWB_LS_MODE = 3; % 2 纯UWB解算采用2D模式 3纯UWB解算采用3D模式
UWB_EKF_UPDATE_MODE = 3; % EKF 融合采用2D模式 3: EKF融合采用3D模式
%% 数据初始化
out_data.uwb = []; % 初始化输出数据结构
out_data.uwb.time = dataset.uwb.time; % 存储UWB时间戳
out_data.imu.time = dataset.imu.time; % 存储IMU时间戳
out_data.uwb.anchor = dataset.uwb.anchor; % 存储基站坐标
% pr = 0;
% last_pr = 0;
%% 滤波参数初始化
settings = uwb_imu_example_settings(); % 获取滤波器设置参数使用uwb_imu_example_settings()函数获取滤波器的配置参数
% 返回值是一个结构体settings包含滤波器的各种参数
R = diag(ones(dataset.uwb.cnt, 1)*settings.sigma_uwb^(2)); % 量测噪声协方差矩阵
noimal_state = init_navigation_state(settings) ; % 初始化名义状态10维位置x,y,z;速度vx,vy,vz;姿态四元数q0,q1,q2,q3
% 名义状态:系统在理想条件下的理论状态,忽略噪声、扰动和模型误差
err_state = zeros(15, 1); % 初始化误差状态,15维位置误差3维速度误差3维姿态误差3维加速度计零偏3维陀螺仪零偏3维
% 误差状态:实际状态与名义状态之间的偏差,由噪声、外部干扰或模型不确定性导致
%使用第一帧伪距作为初始状态
pr = dataset.uwb.tof(:, 1); % 获取第一帧UWB测量数据
% 初始位置解算
% 测量值=真实值+零偏+噪声
% 零偏是传感器在无输入信号时输出的非零偏移量,反应其基准点的系统性偏差
% 伪距是UWB超宽带等无线定位系统中通过信号传播时间计算出的等效距离测量值
% noimal_state前三项:位置坐标,使用最小二乘法解算
noimal_state(1:3) = multilateration(dataset.uwb.anchor, [ 1 1 0.99]', pr', UWB_LS_MODE);
du = zeros(6, 1); % 初始化零偏反馈
[P, Q1, Q2, ~, ~] = init_filter(settings); % 初始化滤波器参数,此处初始化时使用~占位符,因为这个地方这两个参数没有意义,这样占位可以不占用内存
% 输出提示
fprintf("共%d帧数据, IMU采样频率:%d Hz 共运行时间 %d s\n", N, 1 / dt, N * dt);
fprintf("UWB基站个数:%d\n", dataset.uwb.cnt);
fprintf("UWB量测更新频率为:%d Hz\n", (1 / dt) / m_div);
fprintf("UWB EKF量测更新模式: %dD模式\n", UWB_EKF_UPDATE_MODE);
fprintf("纯UWB最小二乘解算: %dD模式\n", UWB_LS_MODE);
fprintf("EKF 滤波参数:\n");
settings
fprintf("开始滤波...\n");
for k=1:N
acc = dataset.imu.acc(:,k);
gyr = dataset.imu.gyr(:,k);
% 反馈 加速度bias, 陀螺bias
% 测量值 = 真实值 + 零偏 + 噪声
acc = acc - du(1:3); % du是零偏1346分别代表加速度和陀螺仪的零偏
gyr = gyr - du(4:6); % 陀螺仪
% 捷联惯导解算
p = noimal_state(1:3); % 位置坐标 [x; y; z](单位:米)
v = noimal_state(4:6); % 速度 [vx; vy; vz]单位m/s
q = noimal_state(7:10); % 姿态四元数 [q0; q1; q2; q3]
% 捷联惯导解算输入当前状态和IMU测量值输出更新后的位置、速度和姿态
% pvq是当前的状态acc、gyr是加速度和陀螺仪的测量值dt为时间步长后面为重力向量
[p, v, q] = nav_equ_local_tan(p, v, q, acc, gyr, dt, [0, 0, -9.8]'); % 东北天坐标系,重力简化为-9.8
% 小车假设基本做平面运动N系下Z轴速度基本为0直接给0
v(3) = 0;
% 更新数值
noimal_state(1:3) = p;
noimal_state(4:6) = v;
noimal_state(7:10) = q;
out_data.eul(k,:) = q2eul(q); % 四元数转欧拉角并存入数据表
% 生成F阵 G阵
% 看函数注释,根据当前的状态、加速度测量值 acc 和采样时间 dt生成状态转移矩阵 F 和过程噪声耦合矩阵 G
[F, G] = state_space_model(noimal_state, acc, dt);
% 卡尔曼P阵预测公式
P = F*P*F' + G*blkdiag(Q1, Q2)*G';
% log数据
out_data.x(k,:) = noimal_state;
out_data.delta_u(k,:) = du'; % 位置方差
out_data.diag_P(k,:) = trace(P); % 姿态四元数方差
%% EKF UWB量测更新
m_div_cntr = m_div_cntr+1; %量测分频器
% 每m_div次量测才更新一次EKF量测(UWB更新)此时m_div设置为1
if m_div_cntr == m_div
m_div_cntr = 0;
pr = dataset.uwb.tof(1:dataset.uwb.cnt, k); % pr是当前时刻第k帧所有UWB基站的TOF
%判断两次PR 差如果差太大则认为这个基站PR比较烂不要了。相当于GNSS里的挑星
% arr = find(abs(pr - last_pr) < 0.05);
% last_pr = pr;
% out_data.good_anchor_cnt(k,:) = length(arr); %记录挑出来的基站数
%
% if(isempty(arr))
% continue;
% end
%
% %构建 剔除不好的基站之后的基站位置矩阵和R矩阵
% pr = pr(arr);
% anch = dataset.uwb.anchor(:, arr);
% R1 = R(arr, arr);
% 算了不挑基站了,所有基站直接参与定位,其实也差不太多
anch = dataset.uwb.anchor;
R1 = R; % 测量噪声协方差矩阵
% 量测方程
% Y是根据当前状态 noimal_state 预测出来的各个基站到目标的距离
% H是观测矩阵表示距离对状态的偏导
[Y, H] = uwb_hx(noimal_state, anch, UWB_EKF_UPDATE_MODE);
% 卡尔曼公式计算K
S = H*P*H'+R1; % system uncertainty 系统不确定度,用于衡量测量是否可信
residual = pr - Y; % residual 或者叫新息 测量与预测的差值
%% 根据量测置信度给R一些增益 Self-Calibrating Multi-Sensor Fusion with Probabilistic
%Measurement Validation for Seamless Sensor Switching on a UAV, 计算量测可靠性
% 衡量量测可信度L越大代表当前量测越不可信Mahalanobis 距离L
L = (residual'*S^(-1)*residual);
out_data.L(k,:) = L;
% if(L > 20 ) %如果量测置信度比较大,则更不相信量测
% S = H*P*H'+R1*5;
% end
K = (P*H')/(S); % 卡尔曼增益K
err_state = [zeros(9,1); du] + K*(residual); % 状态误差向量
% 反馈速度位置
noimal_state(1:6) = noimal_state(1:6) + err_state(1:6);
% 反馈姿态
q = noimal_state(7:10); % 提取姿态四元数
q = qmul(rv2q(err_state(7:9)), q);
noimal_state(7:10) = q; % 姿态反馈(使用小角度修正旋转)
%存储加速度计零偏,陀螺零偏
du = err_state(10:15);
% P阵后验更新
P = (eye(15)-K*H)*P;
end
%% 车载约束Z轴速度约束 B系下 Z轴速度必须为0(不能钻地).. 可以有效防止Z轴位置跳动 参考https://kth.instructure.com/files/677996/download?download_frd=1 和 https://academic.csuohio.edu/simond/pubs/IETKalman.pdf
%% 车辆在 B系Z轴上不应该有速度如果估计有就认为估计错了通过观测去修正
R2 = eye(1)*0.5;
Cn2b = q2m(qconj(noimal_state(7:10))); % 计算四元数的共轭,得到从导航系到机体系的旋转;再转换成方向余弦矩阵
H = [zeros(1,3), [0 0 1]* Cn2b, zeros(1,9)]; % 构造观测矩阵H用于观测当前状态中的速度
K = (P*H')/(H*P*H'+R2);
z = Cn2b*noimal_state(4:6); % 当前估计的B系Z轴速度
err_state = [zeros(9,1); du] + K*(0-z(3:3)); % 目标观测值是0因为车不能在B系Z轴上移动当前估计值是z(3),差值为残差
% 反馈速度位置 更新状态
noimal_state(1:6) = noimal_state(1:6) + err_state(1:6);
% 反馈姿态
q = noimal_state(7:10);
q = qmul(ch_rv2q(err_state(7:9)), q); % 更新原始四元数
noimal_state(7:10) = q;
%存储加速度计零偏,陀螺零偏
% du = err_state(10:15);
% P阵后验更新
P = (eye(15)-K*H)*P;
end
fprintf("开始纯UWB最小二乘位置解算...\n");
%% 纯 UWB 位置解算
j = 1;
uwb_pos = [1 1 1]';
N = length(dataset.uwb.time);
for i=1:N
pr = dataset.uwb.tof(:, i);
% 去除NaN点
%if all(~isnan(pr)) == true
uwb_pos = multilateration(dataset.uwb.anchor, uwb_pos, pr', UWB_LS_MODE);
out_data.uwb.pos(:,j) = uwb_pos;
j = j+1;
%end
end
fprintf("计算完成...\n");
%% plot 数据
out_data.uwb.tof = dataset.uwb.tof;
out_data.uwb.fusion_pos = out_data.x(:,1:3)';
demo_plot(dataset, out_data);
%% 初始化nomial state
function x = init_navigation_state(~)
% 简单点, 全部给 0 。10维位置x,y,z;速度vx,vy,vz;姿态四元数q0,q1,q2,q3
q = eul2q(deg2rad([0 0 0])); % eul2q函数用于将欧拉角转换为四元数
x = [zeros(6,1); q]; % 拼接6维零向量与四元数
end
%% 初始化滤波器参数
function [P, Q1, Q2, R, H] = init_filter(settings)
% Kalman filter state matrix
P = zeros(15);
P(1:3,1:3) = settings.factp(1)^2*eye(3);
P(4:6,4:6) = settings.factp(2)^2*eye(3);
P(7:9,7:9) = diag(settings.factp(3:5)).^2;
P(10:12,10:12) = settings.factp(6)^2*eye(3);
P(13:15,13:15) = settings.factp(7)^2*eye(3);
% Process noise covariance
Q1 = zeros(6);
Q1(1:3,1:3) = diag(settings.sigma_acc).^2*eye(3);
Q1(4:6,4:6) = diag(settings.sigma_gyro).^2*eye(3);
Q2 = zeros(6);
Q2(1:3,1:3) = settings.sigma_acc_bias^2*eye(3);
Q2(4:6,4:6) = settings.sigma_gyro_bias^2*eye(3);
R =0;
H = 0;
end
%% 生成F阵G阵
% 根据当前的状态 x、加速度测量值 acc 和采样时间 dt生成状态转移矩阵 F 和过程噪声耦合矩阵 G
function [F,G] = state_space_model(x, acc, dt)
Cb2n = q2m(x(7:10)); % 把四元数转换成方向余弦矩阵从机体坐标系body转到导航系nav
sk = askew(Cb2n * acc); % 对一个向量v构造出反对称矩阵用于表示向量叉乘的线性化形式
O = zeros(3);
I = eye(3);
% P V theta 加计零偏 陀螺零偏
F = [
O I O O O;
O O -sk -Cb2n O;
O O O O -Cb2n;
O O O O O;
O O O O O];
% Approximation of the discret time state transition matrix
% 将连续时间的状态转移矩阵做一次一阶近似离散化
F = eye(15) + dt*F;
% Noise gain matrix
G=dt*[
O O O O;
Cb2n O O O;
O -Cb2n O O;
O O I O;
O O O I];
end
%% UWB量测过程
% Y 根据当前状态和UWB基站坐标预测出来的伪距
% H 量测矩阵
% anchor: 基站坐标 M x N: M:3(三维坐标) N:基站个数
% dim: 2: 二维 3三维
function [Y, H] = uwb_hx(x, anchor, dim)
N = size(anchor,2); % 基站个数
% 三维取xyz二维取xy
position = x(1:3);
if(dim)== 2
position = position(1:2);
anchor = anchor(1:2, 1:N);
% uwb.anchor
end
Y = []; % Y是伪距预测即目标位置到每个锚点的欧几里得距离
H = []; % 雅可比矩阵的每一行
% 计算预测的伪距s所有预测的位置与各个锚点的向量差
perd_pr = repmat(position,1,N) - anchor(:,1:N);
for i=1:N
if(dim)== 2
H = [H ;perd_pr(:,i)'/norm(perd_pr(:,i)),zeros(1,13)];
else
H = [H ;perd_pr(:,i)'/norm(perd_pr(:,i)),zeros(1,12)];
end
Y = [Y ;norm(perd_pr(:,i))];
end
end

View File

@ -0,0 +1,25 @@
function settings = uwb_imu_example_settings()
settings.sigma_uwb = 0.5; % UWB测距噪声
settings.sigma_acc = 0.02; %加速度计噪声
settings.sigma_gyro = deg2rad(0.3); %陀螺仪噪声
settings.sigma_acc_bias = 0.00; %加速度计零偏随机游走噪声
settings.sigma_gyro_bias = deg2rad(0.0); %陀螺仪零偏随机游走噪声
% Initial Kalman filter uncertainties (standard deviations)
settings.factp(1) = 20; % 初值位置方差(置信度)(m),越大代表对初始位置越不信任
settings.factp(2) = 1; % 初值速度方差( [m/s]
settings.factp(3:5) = deg2rad([1 1 20]); % 初始i姿态方差 (roll,pitch,yaw) [rad]
settings.factp(6) = 0.01; % 初始加速度零偏方差 [m/s^2]
settings.factp(7) = deg2rad(0.01); % 初始角速度零偏方差 [rad/s]
end

25
lib/ENU2LLA.m Normal file
View File

@ -0,0 +1,25 @@
function [lat, lon, h] = ENU2LLA(E, N, U, lat0, lon0, h0)
% ENU 转 经纬高
% lat0, lon0, h0: 起始点经纬高, 经纬度为rad 高度为m
% lat, lon, h 终点经纬度为rad 高度为m
% E, N ,U 系下增量单位为m
%精确算法
% XYZ0 = ch_LLA2ECEF(lat0, lon0, h0);
% XYZ1 = ch_LLA2ECEF(lat, lon, h);
% dXYZ = XYZ1 - XYZ0;
%
% [~, ~, C_ECEF2ENU, ~]= ch_earth(lat0, lon0, h0);
% dENU = C_ECEF2ENU * dXYZ;
% E = dENU(1);
% N= dENU(2);
% U = dENU(3);
%近似算法
R_0 = 6378137; %WGS84 Equatorial radius in meters
clat = cos(lat0);
h = h0 + U;
lon = lon0 + E/clat/R_0;
lat = lat0 + N/R_0 ;
end

108
lib/allan.m Normal file
View File

@ -0,0 +1,108 @@
%% ('NoiseDensity(角度随机游走)', N, 'RandomWalk(角速率随机游走)', K,'BiasInstability(零偏稳定性)', B);
% omega 必须为 rad/s(陀螺) 加速度计: m/s^(2)
% see Freescale AN: Allan Variance: Noise Analysis for Gyroscopes
function [avar, tau, N, K, B] = allan(omega, Fs)
% t0 = 1/Fs;
% theta = cumsum(omega, 1)*t0;
%
% maxNumM = 200;
% L = size(theta, 1);
% maxM = 2.^floor(log2(L/2));
% m = logspace(log10(10), log10(maxM), maxNumM).';
% m = ceil(m); % m must be an integer.
% m = unique(m); % Remove duplicates.
%
% tau = m*t0;
%
% avar = zeros(numel(m), 1);
% for i = 1:numel(m)
% mi = m(i);
% avar(i,:) = sum( ...
% (theta(1+2*mi:L) - 2*theta(1+mi:L-mi) + theta(1:L-2*mi)).^2, 1);
% end
% avar = avar ./ (2*tau.^2 .* (L - 2*m));
[avar,tau] = allanvar(omega, 'octave', Fs);
adev = sqrt(avar);
%% Angle Random Walk
slope = -0.5;
logtau = log10(tau);
logadev = log10(adev);
dlogadev = diff(logadev) ./ diff(logtau);
[~, i] = min(abs(dlogadev - slope));
% Find the y-intercept of the line.
b = logadev(i) - slope*logtau(i);
% Determine the angle random walk coefficient from the line.
logN = slope*log10(1) + b;
N = 10^logN;
% Plot the results.
tauN = 1;
lineN = N ./ sqrt(tau);
%% Rate Random Walk
slope = 0.5;
logtau = log10(tau);
logadev = log10(adev);
dlogadev = diff(logadev) ./ diff(logtau);
[~, i] = min(abs(dlogadev - slope));
% Find the y-intercept of the line.
b = logadev(i) - slope*logtau(i);
% Determine the rate random walk coefficient from the line.
logK = slope*log10(3) + b;
K = 10^logK;
% Plot the results.
tauK = 3;
lineK = K .* sqrt(tau/3);
%% Bias Instability
slope = 0;
logtau = log10(tau);
logadev = log10(adev);
dlogadev = diff(logadev) ./ diff(logtau);
[~, i] = min(abs(dlogadev - slope));
[~, i] = min(adev); %yandld added
% Find the y-intercept of the line.
b = logadev(i) - slope*logtau(i);
% Determine the bias instability coefficient from the line.
scfB = sqrt(2*log(2)/pi);
logB = b - log10(scfB);
B = 10^logB;
% Plot the results.
tauB = tau(i);
lineB = B * scfB * ones(size(tau));
tauParams = [tauN, tauK, tauB];
params = [N, K, scfB*B];
figure;
loglog(tau, adev, '-*', tau, [lineN, lineK, lineB], '--', tauParams, params, 'o');
title('Allan Deviation with Noise Parameters')
xlabel('\tau')
ylabel('\sigma(\tau)')
legend('\sigma', '\sigma_N', '\sigma_K', '\sigma_B')
text(tauParams, params, {'N', 'K', '0.664B'})
grid on
end

12
lib/askew.m Normal file
View File

@ -0,0 +1,12 @@
function m = askew(v)
% 生成反对称矩阵
%
% Input: v - 3x1 vector
% Output: m - v的反对称阵
% | 0 -v(3) v(2) |
% m = | v(3) 0 -v(1) |
% |-v(2) v(1) 0 |
m = [ 0, -v(3), v(2);
v(3), 0, -v(1);
-v(2), v(1), 0 ];

44
lib/att_upt.m Normal file
View File

@ -0,0 +1,44 @@
function out = att_upt(in, gyr, dt)
% 输入当前姿态四元数和陀螺仪角速度,输出更新后的姿态四元数
% in为当前姿态四元数
% gyr是陀螺仪测量值
% dt 时间步长
%% 单子样旋转矢量
rv = gyr*dt; % 旋转矢量 = 角速度 × 时间步长
dq = rv2q(rv); % 调用函数将旋转矢量转为四元数
%% 不专业的做法
% dq(1) = 1;
% dq(2) = rv(1)*0.5;
% dq(3) = rv(2)*0.5;
% dq(4) = rv(3)*0.5;
out = qmul(in, dq); % 四元数相乘 当前姿态 × 增量 = 新姿态
out = qnormlz(out); % 归一化 归一化防止数值漂移
%% 使用旋转矩阵更新
%
% Cb2n = ch_q2m(in);
% theta = gyr*dt;
%
% %C = eye(3) + ch_askew(theta);
% C = ch_rv2m(theta);
%
% Cb2n = Cb2n * C;
%
% % 截断误差,保持正交化 GNSS与惯性及多传感器组合导航系统原理-第二版.pdf 公式 5.80
% c1 = Cb2n(1,:);
% c2 = Cb2n(2,:);
% c3 = Cb2n(3,:);
% c1 = 2 / (1 + dot(c1,c1))*c1;
% c2 = 2 / (1 + dot(c2,c2))*c2;
% c3 = 2 / (1 + dot(c3,c3))*c3;
% Cb2n = [c1; c2; c3];
%
% out = ch_m2q(Cb2n);
end

View File

@ -0,0 +1,34 @@
%% ACC Calibraiton
function [C, B] = acc_calibration(input)
%% OPTION1: ST mehold AN4508 Application note Parameters and calibration of a low-g 3-axis accelerometer
B = [1 0 0; 0 1 0; 0 0 1; -1 0 0; 0 -1 0; 0 0 -1];
A= [input -[ones(length(input), 1)]];
X = inv(A'*A) * A'*B;
C = X(1:3,:)';
B = X(4,:)';
%% OPTION2: Time- and Computation-Efficient Calibration of MEMS 3D Accelerometers and Gyroscopes
% Data: M x N M: sample set(6), N:acc X,Y,Z (3)
% row1 = 1, 0, 0 case
% row2 = 0, 1, 0 case
% row3 = 0, 0, 1 case
% row4 = -1, 0, 0 case
% row5 = 0, -1, 0 case
% row6 = 0, 0, -1 case
% input = input';
% Asp = input(:,1:3);
% Asn = input(:,4:6);
%
% B = ((Asp + Asn)*[1 1 1]' / 6);
% C = 2*(Asp - Asn)^(-1);
end

View File

@ -0,0 +1,112 @@
clear;
clc;
close all;
% %% http://ztrw.mchtr.pw.edu.pl/en/least-squares-circle-fit/
%
%
% input = [
% 9.1667 0.5000 1.0000
% 0.3333 1.8750 1.0000
% -7.8083 7.4167 1.0000
% -10.0167 11.2500 1.0000
% -15.5583 21.3750 1.0000
% -16.7500 31.6250 1.0000
% -13.4333 40.8333 1.0000
% 4.3917 53.0000 1.0000
% 15.3500 54.8750 1.0000
% 21.3083 54.6250 1.0000
% 32.5417 49.2083 1.0000
% 33.0417 38.8333 1.0000
% 32.8750 31.5417 1.0000
% 34.3083 19.3750 1.0000
% 25.2917 11.0417 1.0000
% 16.2500 5.0000 1.0000
% 11.2083 4.0000 1.0000
% ];
%
%
% P = input(:,1:2)';
% n = length(P);
clear;
clc;
close all;
P = [1 7; 2 6; 5 8; 7 7; 9 5; 3 7]';
n= length(P);
plot(P(1,:), P(2,:), '*');
%build deisgn matrix
A = [ P(1,:); P(2,:); ones(1,n)]';
b = sum(P.*P, 1)';
% ls solution
a= (A'*A)^(-1)*A'*b;
xc = 0.5*a(1);
yc = 0.5*a(2);
R = sqrt((a(1)^2+a(2)^2)/4+a(3));
R
viscircles([xc, yc],R);
axis equal
% max_min_ofs = max(P,[],2) + min(P, [], 2) ;
% max_min_ofs = max_min_ofs / 2;
% max_min_ofs
% %¸ø¶¨³õÖµ
% xc = 5.3794;
% yc = 7.2532;
% r = 3.0370;
% res = [xc; yc; r];
%
% max_iters = 20;
%
% max_dif = 10^(-6); % max difference between consecutive results
% for i = 1 : max_iters
% J = Jacobian(res(1), res(2), P);
% R = Residual(res(1), res(2), res(3), P);
% prev = res;
% res = res - (J'*J)^(-1)*J'*R;
% dif = abs((prev - res)./res);
% if dif < max_dif
% fprintf('Convergence met after %d iterations\n', i);
% break;
% end
% end
% if i == max_iters
% fprintf('Convergence not reached after %d iterations\n', i);
% end
%
% xc = res(1);
% yc = res(2);
% r = res(3);
%
% plot(P(:,1), P(:,2), '*')
% viscircles([xc, yc],r);
% axis equal
%
% function J = Jacobian(xc, yc, P)
% len = size(P);
% r = sqrt((xc - P(:,1)).^2 + (yc - P(:,2)).^2);
% J = [(xc - P(:,1))./r, (yc - P(:,2))./r, -ones(len(1), 1)];
% end
function R = Residual(xc, yc, r, P)
R = sqrt((xc - P(:,1)).^2 + (yc - P(:,2)).^2) - r;
end
%

View File

@ -0,0 +1,185 @@
function [A, b, magB, er]=ellipsoid_fit(XYZ,varargin)
% Fit an (non)rotated ellipsoid or sphere to a set of xyz data points
% XYZ: N(rows) x 3(cols), matrix of N data points (x,y,z)
% optional flag f, default to 0 (fitting of rotated ellipsoid)
A = eye(3);
x=XYZ(:,1); y=XYZ(:,2); z=XYZ(:,3);
if nargin>1
f=varargin{1};
else f=0;
end
if( f == 5)
[A, b, magB, er, ispd] = correctEllipsoid4(x,y,z);
end
if( f == 1)
[A, b, magB, er, ispd] = correctEllipsoid7(x,y,z);
end
if( f == 0)
[A, b, magB, er, ispd] = correctEllipsoid10(x,y,z);
end
end
function [Winv, V, B, er, ispd] = correctEllipsoid4(x,y,z)
% R is the identity
b = x.*x + y.*y + z.*z;
A = [x,y,z];
A = [A ones(numel(x),1)];
soln = (A'*A)^(-1)*A'*b;
Winv = eye(3);
V = 0.5*soln(1:3);
B = sqrt(soln(4) + sum(V.*V));
% res = residual(Winv, V, B, [x,y,z])
% er = (1/(2*B*B))*sqrt(res.'*res/numel(x));
if nargout > 3
res = A*soln - b;
er = (1/(2*B*B) * sqrt( res.'*res / numel(x)));
ispd = 1;
else
er = -ones(1);
ispd = -1;
end
end
function [Winv, V, B, er, ispd] = correctEllipsoid7(x,y,z)
d = [x.*x, y.*y, z.*z, x, y, z, ones(size(x))];
dtd = d.' * d;
[evc, evlmtx] = eig(dtd);
eigvals = diag(evlmtx);
[~, idx] = min(eigvals);
beta = evc(:,idx); %solution has smallest eigenvalue
A = diag(beta(1:3));
dA = det(A);
if dA < 0
A = -A;
beta = -beta;
dA = -dA; %Compensate for -A.
end
V = -0.5*(beta(4:6)./beta(1:3)); %hard iron offset
B = sqrt(abs(sum([...
A(1,1)*V(1)*V(1), ...
A(2,2)*V(2)*V(2), ...
A(3,3)*V(3)*V(3), ...
-beta(end)] ...
)));
% We correct Winv and B by det(A) because we don't know which has the
% gain. By convention, normalize A.
det3root = nthroot(dA,3);
det6root = sqrt(det3root);
Winv = sqrtm(A./det3root);
B = B./det6root;
if nargout > 3
res = residual(Winv,V,B, [x,y,z]);
er = (1/(2*B*B))*sqrt(res.'*res/numel(x));
[~,p] = chol(A);
ispd = (p == 0);
else
er = -ones(1, 'like',x);
ispd = -1;
end
end
function [Winv, V,B,er, ispd] = correctEllipsoid10(x,y,z)
d = [...
x.*x, ...
2*x.*y, ...
2*x.*z, ...
y.*y, ...
2*y.*z, ...
z.*z, ...
x, ...
y, ...
z, ...
ones(size(x))];
dtd = d.' * d;
[evc, evlmtx] = eig(dtd);
eigvals = diag(evlmtx);
[~, idx] = min(eigvals);
beta = evc(:,idx); %solution has smallest eigenvalue
A = beta([1 2 3; 2 4 5; 3 5 6]); %make symmetric
dA = det(A);
if dA < 0
A = -A;
beta = -beta;
dA = -dA; %Compensate for -A.
end
V = -0.5*(A^(-1)*beta(7:9)); %hard iron offset
B = sqrt(abs(sum([...
A(1,1)*V(1)*V(1), ...
2*A(2,1)*V(2)*V(1), ...
2*A(3,1)*V(3)*V(1), ...
A(2,2)*V(2)*V(2), ...
2*A(3,2)*V(2)*V(3), ...
A(3,3)*V(3)*V(3), ...
-beta(end)] ...
)));
% We correct Winv and B by det(A) because we don't know which has the
% gain. By convention, normalize A.
det3root = nthroot(dA,3);
det6root = sqrt(det3root);
Winv = sqrtm(A./det3root);
B = B./det6root;
if nargout > 3
res = residual(Winv,V,B, [x,y,z]);
er = (1/(2*B*B))*sqrt(res.'*res/numel(x));
[~,p] = chol(A);
ispd = (p == 0);
else
er = -ones(1, 'like',x);
ispd = -1;
end
end
function r = residual(Winv, V, B, data)
% Residual error after correction
spherept = (Winv * (data.' - V)).'; % a point on the unit sphere
radsq = sum(spherept.^2,2);
r = radsq - B.^2;
end

View File

@ -0,0 +1,207 @@
function [ center, radii, evecs, v, chi2 ] = ellipsoid_fit_new( X, equals )
%
% Fit an ellispoid/sphere/paraboloid/hyperboloid to a set of xyz data points:
%
% [center, radii, evecs, pars ] = ellipsoid_fit( X )
% [center, radii, evecs, pars ] = ellipsoid_fit( [x y z] );
% [center, radii, evecs, pars ] = ellipsoid_fit( X, 1 );
% [center, radii, evecs, pars ] = ellipsoid_fit( X, 2, 'xz' );
% [center, radii, evecs, pars ] = ellipsoid_fit( X, 3 );
%
% Parameters:
% * X, [x y z] - Cartesian data, n x 3 matrix or three n x 1 vectors
% * flag - '' or empty fits an arbitrary ellipsoid (default),
% - 'xy' fits a spheroid with x- and y- radii equal
% - 'xz' fits a spheroid with x- and z- radii equal
% - 'xyz' fits a sphere
% - '0' fits an ellipsoid with its axes aligned along [x y z] axes
% - '0xy' the same with x- and y- radii equal
% - '0xz' the same with x- and z- radii equal
%
% Output:
% * center - ellispoid or other conic center coordinates [xc; yc; zc]
% * radii - ellipsoid or other conic radii [a; b; c]
% * evecs - the radii directions as columns of the 3x3 matrix
% * v - the 10 parameters describing the ellipsoid / conic algebraically:
% Ax^2 + By^2 + Cz^2 + 2Dxy + 2Exz + 2Fyz + 2Gx + 2Hy + 2Iz + J = 0
% * chi2 - residual sum of squared errors (chi^2), this chi2 is in the
% coordinate frame in which the ellipsoid is a unit sphere.
%
% Author:
% Yury Petrov, Oculus VR
% Date:
% September, 2015
%
%reference
narginchk( 1, 3 ) ; % check input arguments
if nargin == 1
equals = ''; % no constraints by default
end
if size( X, 2 ) ~= 3
error( 'Input data must have three columns!' );
else
x = X( :, 1 );
y = X( :, 2 );
z = X( :, 3 );
end
% need nine or more data points
if length( x ) < 9 && strcmp( equals, '' )
error( 'Must have at least 9 points to fit a unique ellipsoid' );
end
if length( x ) < 8 && ( strcmp( equals, 'xy' ) || strcmp( equals, 'xz' ) )
error( 'Must have at least 8 points to fit a unique ellipsoid with two equal radii' );
end
if length( x ) < 6 && strcmp( equals, '0' )
error( 'Must have at least 6 points to fit a unique oriented ellipsoid' );
end
if length( x ) < 5 && ( strcmp( equals, '0xy' ) || strcmp( equals, '0xz' ) )
error( 'Must have at least 5 points to fit a unique oriented ellipsoid with two equal radii' );
end
if length( x ) < 4 && strcmp( equals, 'xyz' );
error( 'Must have at least 4 points to fit a unique sphere' );
end
% fit ellipsoid in the form Ax^2 + By^2 + Cz^2 + 2Dxy + 2Exz + 2Fyz + 2Gx +
% 2Hy + 2Iz + J = 0 and A + B + C = 3 constraint removing one extra
% parameter
if strcmp( equals, '' )
D = [ x .* x + y .* y - 2 * z .* z, ...
x .* x + z .* z - 2 * y .* y, ...
2 * x .* y, ...
2 * x .* z, ...
2 * y .* z, ...
2 * x, ...
2 * y, ...
2 * z, ...
1 + 0 * x ]; % ndatapoints x 9 ellipsoid parameters
elseif strcmp( equals, 'xy' )
D = [ x .* x + y .* y - 2 * z .* z, ...
2 * x .* y, ...
2 * x .* z, ...
2 * y .* z, ...
2 * x, ...
2 * y, ...
2 * z, ...
1 + 0 * x ]; % ndatapoints x 8 ellipsoid parameters
elseif strcmp( equals, 'xz' )
D = [ x .* x + z .* z - 2 * y .* y, ...
2 * x .* y, ...
2 * x .* z, ...
2 * y .* z, ...
2 * x, ...
2 * y, ...
2 * z, ...
1 + 0 * x ]; % ndatapoints x 8 ellipsoid parameters
% fit ellipsoid in the form Ax^2 + By^2 + Cz^2 + 2Gx + 2Hy + 2Iz = 1
elseif strcmp( equals, '0' )
D = [ x .* x + y .* y - 2 * z .* z, ...
x .* x + z .* z - 2 * y .* y, ...
2 * x, ...
2 * y, ...
2 * z, ...
1 + 0 * x ]; % ndatapoints x 6 ellipsoid parameters
% fit ellipsoid in the form Ax^2 + By^2 + Cz^2 + 2Gx + 2Hy + 2Iz = 1,
% where A = B or B = C or A = C
elseif strcmp( equals, '0xy' )
D = [ x .* x + y .* y - 2 * z .* z, ...
2 * x, ...
2 * y, ...
2 * z, ...
1 + 0 * x ]; % ndatapoints x 5 ellipsoid parameters
elseif strcmp( equals, '0xz' )
D = [ x .* x + z .* z - 2 * y .* y, ...
2 * x, ...
2 * y, ...
2 * z, ...
1 + 0 * x ]; % ndatapoints x 5 ellipsoid parameters
% fit sphere in the form A(x^2 + y^2 + z^2) + 2Gx + 2Hy + 2Iz = 1
elseif strcmp( equals, 'xyz' )
D = [ 2 * x, ...
2 * y, ...
2 * z, ...
1 + 0 * x ]; % ndatapoints x 4 ellipsoid parameters
else
error( [ 'Unknown parameter value ' equals '!' ] );
end
% solve the normal system of equations
d2 = x .* x + y .* y + z .* z; % the RHS of the llsq problem (y's)
u = ( D' * D ) \ ( D' * d2 ); % solution to the normal equations
% find the residual sum of errors
% chi2 = sum( ( 1 - ( D * u ) ./ d2 ).^2 ); % this chi2 is in the coordinate frame in which the ellipsoid is a unit sphere.
% find the ellipsoid parameters
% convert back to the conventional algebraic form
if strcmp( equals, '' )
v(1) = u(1) + u(2) - 1;
v(2) = u(1) - 2 * u(2) - 1;
v(3) = u(2) - 2 * u(1) - 1;
v( 4 : 10 ) = u( 3 : 9 );
elseif strcmp( equals, 'xy' )
v(1) = u(1) - 1;
v(2) = u(1) - 1;
v(3) = -2 * u(1) - 1;
v( 4 : 10 ) = u( 2 : 8 );
elseif strcmp( equals, 'xz' )
v(1) = u(1) - 1;
v(2) = -2 * u(1) - 1;
v(3) = u(1) - 1;
v( 4 : 10 ) = u( 2 : 8 );
elseif strcmp( equals, '0' )
v(1) = u(1) + u(2) - 1;
v(2) = u(1) - 2 * u(2) - 1;
v(3) = u(2) - 2 * u(1) - 1;
v = [ v(1) v(2) v(3) 0 0 0 u( 3 : 6 )' ];
elseif strcmp( equals, '0xy' )
v(1) = u(1) - 1;
v(2) = u(1) - 1;
v(3) = -2 * u(1) - 1;
v = [ v(1) v(2) v(3) 0 0 0 u( 2 : 5 )' ];
elseif strcmp( equals, '0xz' )
v(1) = u(1) - 1;
v(2) = -2 * u(1) - 1;
v(3) = u(1) - 1;
v = [ v(1) v(2) v(3) 0 0 0 u( 2 : 5 )' ];
elseif strcmp( equals, 'xyz' )
v = [ -1 -1 -1 0 0 0 u( 1 : 4 )' ];
end
v = v';
% form the algebraic form of the ellipsoid
A = [ v(1) v(4) v(5) v(7); ...
v(4) v(2) v(6) v(8); ...
v(5) v(6) v(3) v(9); ...
v(7) v(8) v(9) v(10) ];
% find the center of the ellipsoid
center = -A( 1:3, 1:3 ) \ v( 7:9 );
% form the corresponding translation matrix
T = eye( 4 );
T( 4, 1:3 ) = center';
% translate to the center
R = T * A * T';
% solve the eigenproblem
[ evecs, evals ] = eig( R( 1:3, 1:3 ) / -R( 4, 4 ) );
radii = sqrt( 1 ./ diag( abs( evals ) ) );
sgns = sign( diag( evals ) );
radii = radii .* sgns;
% calculate difference of the fitted points from the actual data normalized by the conic radii
d = [ x - center(1), y - center(2), z - center(3) ]; % shift data to origin
d = d * evecs; % rotate to cardinal axes of the conic;
d = [ d(:,1) / radii(1), d(:,2) / radii(2), d(:,3) / radii(3) ]; % normalize to the conic radii
chi2 = sum( abs( 1+0*x - sum( d.^2, 2 ) ) );
if abs( v(end) ) > 1e-6
v = -v / v(end); % normalize to the more conventional form with constant term = -1
else
v = -sign( v(end) ) * v;
end

View File

@ -0,0 +1,29 @@
%% GYR Calibraiton
% example:
% input = [
% 65.2191 0.1712 0.4618
% -0.0901 71.9079 -0.0728
% -0.5425 -0.1436 71.7273
% -65.0873 -0.1008 -0.4814
% 0.1573 -71.9432 0.0436
% 0.6128 0.2009 -71.7076
% ];
%
% theory = [65.5618 0 0; 0 72.0649 0; 0 0 72.1298 ; -65.5081 0 0; 0 -72.1298 0; 0 0 -72.0649];
% gyr_calibration(input, theory)
function [C, B] = gyr_calibration(input, ref)
%% ST mehold AN4508 Application note Parameters and calibration of a low-g 3-axis accelerometer
A= [input -[ones(length(input), 1)]];
B = ref;
X = inv(A'*A) * A'*B;
C = X(1:3,:)';
B = X(4,:)';
%% Time- and Computation-Efficient Calibration of MEMS 3D Accelerometers and Gyroscopes
% TBD
end

53
lib/data_import.m Normal file
View File

@ -0,0 +1,53 @@
function dataset = data_import(path)
tbl = readtable(path);
if sum(ismember(tbl.Properties.VariableNames,'AccX'))
dataset.imu.acc(1,:) = tbl.AccX';
end
if sum(ismember(tbl.Properties.VariableNames,'AccY'))
dataset.imu.acc(2,:) = tbl.AccY';
end
if sum(ismember(tbl.Properties.VariableNames,'AccZ'))
dataset.imu.acc(3,:) = tbl.AccZ';
end
if sum(ismember(tbl.Properties.VariableNames,'GyrX'))
dataset.imu.gyr(1,:) = tbl.GyrX';
end
if sum(ismember(tbl.Properties.VariableNames,'GyrY'))
dataset.imu.gyr(2,:) = tbl.GyrY';
end
if sum(ismember(tbl.Properties.VariableNames,'GyrZ'))
dataset.imu.gyr(3,:) = tbl.GyrZ';
end
if sum(ismember(tbl.Properties.VariableNames,'MagX'))
dataset.imu.mag(1,:) = tbl.MagX';
end
if sum(ismember(tbl.Properties.VariableNames,'MagY'))
dataset.imu.mag(2,:) = tbl.MagY';
end
if sum(ismember(tbl.Properties.VariableNames,'MagZ'))
dataset.imu.mag(3,:) = tbl.MagZ';
end
if sum(ismember(tbl.Properties.VariableNames,'Roll'))
dataset.eul.roll = tbl.Roll';
end
if sum(ismember(tbl.Properties.VariableNames,'Pitch'))
dataset.eul.pitch = tbl.Pitch';
end
if sum(ismember(tbl.Properties.VariableNames,'Yaw'))
dataset.eul.yaw = tbl.Yaw';
end

138
lib/dip13.m Normal file
View File

@ -0,0 +1,138 @@
% syms b1 b2 b3 real
% syms g1 g2 g3 real
% syms v1 v2 v3 real
% syms lambda real
% syms h
% syms l11 l12 l13 l21 l22 l23 l31 l32 l33 real
% L = [l11 l23 l13; l21 l22 l23; l31 l32 l33];
% B = [b1 b2 b3]';
% V = [v1 v2 v3]';
% G = [g1 g2 g3]';
%
% f = V'*L'*L*V - 2*V'*L'*L*B + B'*L'*L*B - h^2
% j = jacobian(f, [l11 l12 l13 l21 l22 l23 l31 l32 l33 b1 b2 b3 lamda]);
% collect(j(1,1), [l11])
%
% alp = L*(V - B);
% beta = V-B;
% 2*alp(1)*beta(1);
% collect(ans,[l11])
function [mis, bias, lamda, inter, residual] = dip13 (acc, mag, mag_norm, epsilon)
%函数的功能using DIP(dual inner product mehold to caluate mag)
%函数的描述: Calibration of tri-axial magnetometer using vector observations and inner products or Calibration and Alignment of Tri-Axial Magnetometers for Attitude Determination
%函数的使用:[mis, bias, lamda, inter, J] = dip (acc, mag, mag_norm, epsilon)
%输入:
% input1: acc: raw acc
% input2: mag: raw mag
%输出:
% mis: misalign matrix
% bias: bias
% lamda: |reference_vector| * |mag_vector| * cos(theta)
% inter: interation
% J: cost function array
mis = eye(3);
bias = zeros(1,3);
lamda = cos(deg2rad(60));
mu = 0.001; % damp
inter = 100;
last_e = 100;
last_J = 100;
% [l11 l12 l13 l21 l22 l23 l31 l32 l33 b1 b2 b3 lamda], lamba = cos(inclincation)
x = [1 0 0 0 1 0 0 0 1 0 0 0 1];
% using max-min mehold to get a inital bias
x(10:12) = (max(mag) + min(mag)) / 2;
[last_J, ~, ~] = lm_dip(x, mag, acc, mag_norm);
for i = 1:inter
[residual(i), Jacobi, e] = lm_dip(x, mag, acc, mag_norm);
x = x - (inv((Jacobi' * Jacobi + mu * eye(length(x)))) * Jacobi' * e)';
%x = x - mu*(Jacobi' * e)';
if(residual(i) <= last_J)
mu = 0.1 * mu;
else
mu = 10 * mu;
end
if((abs(norm(e) - norm(last_e))) < epsilon)
mis =[x(1:3); x(4:6); x(7:9)];
bias = x(10:12)';
lamda = x(13);
inter = i;
break;
end
last_e = e;
last_J = residual(i);
end
end
%% Function
% Jval: error
% gradient : gradient of cost func
% e: Fx
function[residual, gradient, e]=lm_dip(x, mB, gB, h)
n = length(mB);
e = zeros(n*2, 1);
gradient = zeros(n*2, 13);
L =[x(1:3); x(4:6); x(7:9)];
b = x(10:12)';
for i = 1:n
v = mB(i, :)';
g = gB(i , :)';
% caluate e
e(i , :) = v'*L'*L*v -2*v'*L'*L*b + b'*L'*L*b - h^2;
% e(i , :) = (L*(v - b))' * (L*(v - b)) - h^2;
% e(n+ i, :) = g'*L*v - g'*L*b - h*norm(g)*x(13);
e(n+ i, :) = g'*L*(v-b) - h*norm(g)*x(13);
alpa = L * (v - b);
beta = v - b;
% caluate J 1- r
gradient(i, 1) = 2 * alpa(1) * beta(1);
gradient(i, 2) = 2 * alpa(1) * beta(2);
gradient(i, 3) = 2 * alpa(1) * beta(3);
gradient(i, 4) = 2 * alpa(2) * beta(1);
gradient(i, 5) = 2 * alpa(2) * beta(2);
gradient(i, 6) = 2 * alpa(2) * beta(3);
gradient(i, 7) = 2 * alpa(3) * beta(1);
gradient(i, 8) = 2 * alpa(3) * beta(2);
gradient(i, 9) = 2 * alpa(3) * beta(3);
gradient(i, 10) = -2 * (alpa(1) * L(1,1) + alpa(2) * L(2,1) + alpa(3) * L(3,1));
gradient(i, 11) = -2 * (alpa(1) * L(1,2) + alpa(2) * L(2,2) + alpa(3) * L(3,2));
gradient(i, 12) = -2 * (alpa(1) * L(1,3) + alpa(2) * L(2,3) + alpa(3) * L(3,3));
gradient(i, 13) = 0;
% caluate J r- 2r
gradient(n + i, 1) = g(1) * beta(1);
gradient(n + i, 2) = g(1) * beta(2);
gradient(n + i, 3) = g(1) * beta(3);
gradient(n + i, 4) = g(2) * beta(1);
gradient(n + i, 5) = g(2) * beta(2);
gradient(n + i, 6) = g(2) * beta(3);
gradient(n + i, 7) = g(3) * beta(1);
gradient(n + i, 8) = g(3) * beta(2);
gradient(n + i, 9) = g(3) * beta(3);
gradient(n + i, 10) = - (g(1) * L(1,1) + g(2) * L(2,1) + g(3) * L(3,1));
gradient(n + i, 11) = - (g(1) * L(1,2) + g(2) * L(2,2) + g(3) * L(3,2));
gradient(n + i, 12) = - (g(1) * L(1,3) + g(2) * L(2,3) + g(3) * L(3,3));
gradient(n + i, 13) = -norm(v) * norm(g);
end
residual = e' * e;
end

98
lib/dip5.m Normal file
View File

@ -0,0 +1,98 @@
% using DIP mehold, only compute bias, norm_h, lamda(cos(inclination)),
% modifed from DIP13
% %% moudle DIP5
% syms b1 b2 b3 real
% syms g1 g2 g3 real
% syms v1 v2 v3 real
% syms lambda real
% syms norm_h norm_g real
% B = [b1 b2 b3]';
% V = [v1 v2 v3]';
% G = [g1 g2 g3]';
% % %G'*(V - B) - CONST
%
% f1= (V - B)' * (V - B) - norm_h^2;
% j1 = jacobian(f1, [B' norm_h lambda]);
% collect(j1, B) ;
%
% f2 = G' * (V - B) - norm_g * norm_h * lambda;
%
% f = f1 + f2;
% j2 = jacobian(f2, [B' norm_h lambda]);
% collect(j2, B);
%
%
% f1
% j1
%
% f2
% j2
function [mis, bias, norm_h, lamda, inter, J] = dip5 (acc, mag, x, epsilon)
% initalize value
mis = eye(3);
bias = zeros(1,3);
lamda = cos(deg2rad(60));
mu = 0.000; % damp
inter = 100;
last_e = 100;
for i = 1:inter
[J(i), Jacobi, e] = compute_cost_and_jacob (x, mag, acc);
x = x - (inv((Jacobi' * Jacobi + mu * eye(length(x)))) * Jacobi' * e)';
if((abs(norm(e) - norm(last_e))) < epsilon)
bias = x(1:3)';
lamda = x(5);
inter = i;
norm_h = x(4);
break;
end
last_e = e;
end
end
%% Function
% residual: error
% J : gradient of cost func
% e: bx by bz lamada
% X: [Bx By Bz norm_h lambada]
function[residual, J, e]=compute_cost_and_jacob(x, mB, gB)
n = length(mB);
e = zeros(n * 2, 1);
J = zeros(n * 2, 5);
b = x(1:3)';
norm_h = x(4);
lamda = x(5);
for i = 1:n
v = mB(i, :)';
g = gB(i , :)';
% caluate e
e(i , :) = (v - b)' * (v - b) - norm_h^2;
e(n+ i, :) = g' * (v - b) - (norm_h * norm(g) * lamda);
% caluate J 1- r
J(i, 1) = 2 * (b(1) - v(1));
J(i, 2) = 2 * (b(2) - v(2));
J(i, 3) = 2 * (b(3) - v(3));
J(i, 4) = -2 * norm_h;
J(i, 5) = 0;
% caluate J r- 2r
J(n + i, 1) = -g(1);
J(n + i, 2) = -g(2);
J(n + i, 3) = -g(3);
J(n + i, 4) = -lamda * norm(g);
J(n + i, 5) = -norm_h * norm(g);
end
residual = e' * e;
end

57
lib/dpi.m Normal file
View File

@ -0,0 +1,57 @@
%% A new calibration method for tri-axial field sensors in strap-down navigation systems µã»ý²»±ä·¨
% Accelerometer: acc is sensor frame, n= number of obs, m = 3(x y z)
% Magnetometer: raw mag in sensor frame
% A: mag misalignment matrix
% b: mag bias
function [mis bias] = dpi (acc, mag, dot_product)
nbos = length(acc);
% x = l11 l12 l13 l21 l22 l23 l31 l32 l33 b1 b2 b3
A(:,1) = acc(:,1).* mag(:,1);
A(:,2) = acc(:,1).* mag(:,2);
A(:,3) = acc(:,1).* mag(:,3);
A(:,4) = acc(:,2).* mag(:,1);
A(:,5) = acc(:,2).* mag(:,2);
A(:,6) = acc(:,2).* mag(:,3);
A(:,7) = acc(:,3).* mag(:,1);
A(:,8) = acc(:,3).* mag(:,2);
A(:,9) = acc(:,3).* mag(:,3);
A(:,10) = -acc(:,1);
A(:,11) = -acc(:,2);
A(:,12) = -acc(:,3);
%Y = ones(length(gB),1) * dot(gmOb, gReference);
Y = ones(nbos,1) * dot_product;
B = inv(A'*A)*A'*Y;
% or you can use lsqlin(A,Y)
mis = [B(1:3)'; B(4:6)'; B(7:9)'];
bias = [B(10) B(11) B(12)];
end
%% DPI module
% syms l11 l12 l13 l21 l22 l23 l31 l32 l33 real
% syms b1 b2 b3 real
% syms g1 g2 g3 real
% syms v1 v2 v3 real
% syms CONST real
% b: bias
% l11 - l33: mis align
% v magnormetor reading
% g graivity
% model
% L = sym('l%d%d', [3 3]);
% B = [b1 b2 b3]';
% V = [v1 v2 v3]';
% G = [g1 g2 g3]';
% G'*(L*V - B) - CONST
% %G'*(V - B) - CONST
%
% collect(ans, [L B])

8
lib/dv2atti.m Normal file
View File

@ -0,0 +1,8 @@
function [Cb2n] = dv2atti(vn1, vn2, vb1, vb2)
vntmp1 = cross(vn1,vn2); vntmp2 = cross(vntmp1,vn1);
vbtmp1 = cross(vb1,vb2); vbtmp2 = cross(vbtmp1,vb1);
Cb2n = [vn1/norm(vn1), vntmp1/norm(vntmp1), vntmp2/norm(vntmp2)]*...
[vb1/norm(vb1), vbtmp1/norm(vbtmp1), vbtmp2/norm(vbtmp2)]';

11
lib/geo/ch_ECEF2ENU.m Normal file
View File

@ -0,0 +1,11 @@
function [E, N, U] = ch_ECEF2ENU(XYZ, lat0, lon0, h0)
% ECEF坐标转ENU
% lat0, lon0, h0: 起始点经纬高, 经纬度为rad 高度为m
% XYZ ECEF坐标 单位为m
% ENU距离 单位为m
[lat, lon, h] = ch_ECEF2LLA(XYZ);
[E, N, U] = ch_LLA2ENU(lat, lon, h, lat0, lon0, h0);
end

46
lib/geo/ch_ECEF2LLA.m Normal file
View File

@ -0,0 +1,46 @@
function [lat, lon, h] = ch_ECEF2LLA(XYZ)
% ECEF坐标转经纬高
% lat:纬度(rad)
% lon:经度(rad)
% h高度(m)
R_0 = 6378137; %WGS84 Equatorial radius in meters
e = 0.0818191908425; %WGS84 eccentricity
lon = atan2(XYZ(2),XYZ(1));
% From (C.29) and (C.30)
k1 = sqrt(1 - e^2) * abs (XYZ(3));
k2 = e^2 * R_0;
beta = sqrt(XYZ(1)^2 + XYZ(2)^2);
E = (k1 - k2) / beta;
F = (k1 + k2) / beta;
% From (C.31)
P = 4/3 * (E*F + 1);
% From (C.32)
Q = 2 * (E^2 - F^2);
% From (C.33)
D = P^3 + Q^2;
% From (C.34)
V = (sqrt(D) - Q)^(1/3) - (sqrt(D) + Q)^(1/3);
% From (C.35)
G = 0.5 * (sqrt(E^2 + V) + E);
% From (C.36)
T = sqrt(G^2 + (F - V * G) / (2 * G - E)) - G;
% From (C.37)
lat = sign(XYZ(3)) * atan((1 - T^2) / (2 * T * sqrt (1 - e^2)));
% From (C.38)
h = (beta - R_0 * T) * cos(lat) +...
(XYZ(3) - sign(XYZ(3)) * R_0 * sqrt(1 - e^2)) * sin (lat);
end

10
lib/geo/ch_ENU2ECEF.m Normal file
View File

@ -0,0 +1,10 @@
function XYZ = ch_ENU2ECEF(E, N, U, lat0, lon0, h0)
% ENU 转 ECEF
% lat0, lon0, h0: 起始点经纬高, 经纬度为rad 高度为m
% ENU东北天距离单位为m
% 返回 ECEF坐标 单位m
[lat, lon, h] = ch_ENU2LLA(E, N, U, lat0, lon0, h0);
XYZ = ch_LLA2ECEF(lat, lon, h);
end

30
lib/geo/ch_ENU2LLA.m Normal file
View File

@ -0,0 +1,30 @@
function [lat, lon, h] = ch_ENU2LLA(E, N ,U, lat0, lon0, h0)
% ENU 转 经纬高
% E, N ,U 系下增量单位为m
% lat0, lon0, h0: 起始点经纬高, 经纬度为rad 高度为m
% lat, lon, h 终点经纬高, 经纬度为rad 高度为m
%精确解
XYZ0 = ch_LLA2ECEF(lat0, lon0, h0);
[~, ~, C_ECEF2ENU, ~]= ch_earth(lat0, lon0, h0);
dXYZ = C_ECEF2ENU' * [E N U]';
XYZ = dXYZ + XYZ0;
[lat, lon, h] = ch_ECEF2LLA(XYZ);
% % 近似算法
% R_0 = 6378137; %WGS84 Equatorial radius in meters
% clat = cos(lat0);
%
% dlon = E / (R_0 * clat);
% dlat = N / R_0;
% dh = U;
%
% lon = lon0 + dlon;
% lat = lat0 + dlat;
% h = h0 + dh;
end

24
lib/geo/ch_LLA2ECEF.m Normal file
View File

@ -0,0 +1,24 @@
function [XYZ] = ch_LLA2ECEF(lat, lon, h)
% 经纬高转ECEF坐标
% lat:纬度(rad)
% lon:经度(rad)
% h高度(m)
R0 = 6378137; %WGS84 Equatorial radius in meters
e = 0.0818191908425; %WGS84 eccentricity
% Calculate transverse radius of curvature using (2.105)
RN = R0 / sqrt(1 - (e * sin(lat))^2);
% Convert position using (2.112)
clat = cos(lat);
slat = sin(lat);
clon = cos(lon);
slon = sin(lon);
XYZ = [0 0 0]';
XYZ(1) = (RN + h) * clat * clon;
XYZ(2) = (RN + h) * clat * slon;
XYZ(3) = ((1 - e^2) * RN + h) * slat;

25
lib/geo/ch_LLA2ENU.m Normal file
View File

@ -0,0 +1,25 @@
function [E, N, U] = ch_LLA2ENU(lat, lon, h, lat0, lon0, h0)
% 经纬高 转 ENU
% lat0, lon0, h0: 起始点经纬高, 经纬度为rad 高度为m
% lat, lon, h 终点经纬高, 经纬度为rad 高度为m
% E, N ,U 系下增量单位为m
%精确算法
% XYZ0 = ch_LLA2ECEF(lat0, lon0, h0);
% XYZ1 = ch_LLA2ECEF(lat, lon, h);
% dXYZ = XYZ1 - XYZ0;
%
% [~, ~, C_ECEF2ENU, ~]= ch_earth(lat0, lon0, h0);
% dENU = C_ECEF2ENU * dXYZ;
% E = dENU(1);
% N= dENU(2);
% U = dENU(3);
%近似算法
R_0 = 6378137; %WGS84 Equatorial radius in meters
clat = cos(lat0);
E = (lon - lon0) * clat * R_0;
N = (lat - lat0) * R_0;
U = h - h0;
end

40
lib/geo/ch_earth.m Normal file
View File

@ -0,0 +1,40 @@
function [Rns, Rew, C_ECEF2ENU, C_ECEF2NED]= ch_earth(lat, lon, h)
%% 根据经纬度计算地球常用参数
% INPUT
% lat: 纬度(rad)
% lon: 经度(rad)
% OUTPUT
% Rns, R_meridian(RM, ns) 南北向地球曲率半径, 子午圈曲率半径(竖着的)
% Rew_transverse(RN, ew):东西向地球曲率半径, 卯酉圈曲率半径(横着的)
% C_ECEF2ENU: ECEF到ENU转换矩阵
% C_ECEF2NED: ECEF到NED转换矩阵
R0 = 6378137; %WGS84 赤道半径
e = 0.0818191908425; %WGS84 eccentricity
% Calculate meridian radius of curvature using (2.105)
temp = 1 - (e * sin(lat))^2;
Rns = R0 * (1 - e^2) / temp^1.5;
% Calculate transverse radius of curvature using (2.105)
Rew = R0 / sqrt(temp);
clat = cos(lat);
slat = sin(lat);
clon = cos(lon);
slon = sin(lon);
C_ECEF2ENU(1,:) = [-slon , clon, 0];
C_ECEF2ENU(2,:) = [ -slat*clon, -slat*slon clat];
C_ECEF2ENU(3,:) = [ clat*clon, clat*slon, slat];
C_ECEF2NED(1,:) = [-slat*clon, -slat * slon, clat];
C_ECEF2NED(2,:) = [-slon, clon, 0];
C_ECEF2NED(3,:) = [ -clat*clon, -clat*slon, -slat];
end

10
lib/geo/ch_gravity.m Normal file
View File

@ -0,0 +1,10 @@
function gravity = ch_gravity(lat, h)
% gravity: 计算重力(暂时不考虑高度)
%
% INPUT
% lat,纬度(rad)
% h, 高度(m), 暂时没有用
gravity = 9.780325 * ( 1 + 0.0053024*sin(lat)^(2) - 0.0000058*sin(lat*2)^(2));

15
lib/gnss/UTC2GPST.m Normal file
View File

@ -0,0 +1,15 @@
function [gps_week , gps_sec]=UTC2GPST(y, m, d, h, min, s)
% UTC转GPST
if y<90
y=y+2000;
else
y=y+1900;
end
if m<=2
y=y-1;m=m+12;
end
MJD=fix(365.25*y)+fix(30.6001*(m+1))+d+(h+(min+s/60)/60)/24+1720981.5-2400000.5;
gps_week=fix((MJD-44244)/7);
gps_sec=fix(mod((MJD-44244),7))*86400+h*3600+min*60+s;

43
lib/gnss/anheader.m Normal file
View File

@ -0,0 +1,43 @@
function [Obs_types, ant_delta,ifound_types,eof] = anheader(file)
%ANHEADER Analyzes the header of a RINEX file and outputs
% the list of observation types and antenna offset.
% End of file is flagged 1, else 0. Likewise for the types.
% Typical call: anheader('pta.96o')
%Kai Borre 09-12-96
%Copyright (c) by Kai Borre
%$Revision: 1.0 $ $Date: 1997/09/23 $
fid = fopen(file,'rt');
eof = 0;
ifound_types = 0;
Obs_types = [];
ant_delta = [];
while 1 % Gobbling the header
line = fgetl(fid);
answer = findstr(line,'END OF HEADER');
if ~isempty(answer), break; end;
if (line == -1), eof = 1; break; end;
answer = findstr(line,'ANTENNA: DELTA H/E/N');
if ~isempty(answer)
for k = 1:3
[delta, line] = strtok(line);
del = str2num(delta);
ant_delta = [ant_delta del];
end;
end
answer = findstr(line,'# / TYPES OF OBSERV');
if ~isempty(answer)
[NObs, line] = strtok(line);
NoObs = str2num(NObs);
for k = 1:NoObs
[ot, line] = strtok(line);
Obs_types = [Obs_types ot];
end;
ifound_types = 1;
end;
end;
%fclose(fid);
%%%%%%%%% end anheader.m %%%%%%%%%

92
lib/gnss/cal2gpstime.m Normal file
View File

@ -0,0 +1,92 @@
%*******************************************************
% DESCRIPTION:
% This function converts calendar date/time
% to GPS week/time.
%
% ARGUMENTS:
% Either 1) a single n x 6 matrix or
% 2) six separate variables that are
% equal dimensioned vectors
%
% Representing:
% year - Two digit calendar year representing the
% range 1980-2079
% (e.g., 99 = 1999 and 01 = 2001).
% month - calendar month, must be in range 1-12.
% day - calendar day, must be in range 1-31.
% hour - hour (UTC), must be in range 0-24.
% min - minutes (UTC), must be in range 0-59.
% sec - seconds (UTC), must be in range 0-59.
%
% [ Note: all arguments must be integers! ]
%
% OUTPUT:
% gps_week - integer GPS week (does not take
% "rollover" into account).
% gps_seconds - integer seconds elapsed in gps_week.
%
% REFERENCE:
% ASEN 5090 class notes (Spring 2003).
%
% MODIFICATIONS:
% 03-14-06 : Jan Weiss - Original/modified
% from old code.
%
% Colorado Center for Astrodynamics Research
% Copyright 2006 University of Colorado, Boulder
%*******************************************************
function [ gps_week, gps_seconds ] = cal2gpstime(varargin)
% Unpack
if nargin == 1
cal_time = varargin{1};
year = cal_time(:,1);
month = cal_time(:,2);
day = cal_time(:,3);
hour = cal_time(:,4);
min = cal_time(:,5);
sec = cal_time(:,6);
clear cal_time
else
year = varargin{1};
month = varargin{2};
day = varargin{3};
hour = varargin{4};
min = varargin{5};
sec = varargin{6};
end
% Seconds in one week
secs_per_week = 604800;
% Converts the two digit year to a four digit year.
% Two digit year represents a year in the range 1980-2079.
if (year >= 80 & year <= 99)
year = 1900 + year;
end
if (year >= 0 & year <= 79)
year = 2000 + year;
end
% Calculates the 'm' term used below from the given calendar month.
if (month <= 2)
y = year - 1;
m = month + 12;
end
if (month > 2)
y = year;
m = month;
end
% Computes the Julian date corresponding to the given calendar date.
JD = floor( (365.25 * y) ) + floor( (30.6001 * (m+1)) ) + ...
day + ( (hour + min / 60 + sec / 3600) / 24 ) + 1720981.5;
% Computes the GPS week corresponding to the given calendar date.
gps_week = floor( (JD - 2444244.5) / 7 );
% Computes the GPS seconds corresponding to the given calendar date.
gps_seconds=round(((((JD-2444244.5)/7)-gps_week)*secs_per_week)/0.5)*0.5;

12
lib/gnss/ch_gpsdop.m Normal file
View File

@ -0,0 +1,12 @@
function [VDOP, HDOP, PDOP, GDOP] = ch_gpsdop(G, lat, lon)
% GPS原理及接收机设计 谢刚 DOP章节
S = [-sin(lon) cos(lon) 0; -sin(lat)*cos(lon) -sin(lat)*sin(lon) cos(lat); cos(lat)*cos(lon) cos(lat)*sin(lon) sin(lat)];
S = [S [0 0 0]'; [0 0 0 1]];
H = (G'*G)^(-1);
H = S*H*S';
VDOP = sqrt(H(3,3));
HDOP = sqrt(H(1,1) + H(2,2));
PDOP = sqrt (H(1,1) + H(2,2) + H(3,3));
GDOP = sqrt(trace(H));
end

43
lib/gnss/ch_gpsls.m Normal file
View File

@ -0,0 +1,43 @@
function [X, dx, G] = ch_gpsls(X, sat_pos, pr)
% GPS 伪距最小二乘法求解, 状态量为 X Y Z B(钟差)
% X: X 值(1:3) 位置delta, (4) 用户钟差偏差
% G: 设计矩阵
% pr: 校正后的伪距
% sat_pos: 卫星位置矩阵
% delta: delta 值(1:3) 位置delta, (4) 用户钟差偏差
B1=1;
END_LOOP=100;
%卫星个数
n = size(sat_pos, 2);
if n < 4
dx = 0;
G = 0;
return
end
for loop = 1:10
% 获得当前位置与各个基站的距离
r = vecnorm(sat_pos - X(1:3));
% 求得H矩阵
H = (sat_pos - X(1:3)) ./ r;
H =-H';
H = [H(:,1:3), ones(n,1)];
dp = ((pr - r) - X(4))';
% 迭代用户距离
dx = (H'*H)^(-1)*H'*dp;
X = X + dx;
G = H;
% END_LOOP = vnorm(delta(1:3));
end
end

78
lib/gnss/ch_sat_pos.m Normal file
View File

@ -0,0 +1,78 @@
function [XYZ, sv_dt]=ch_sat_pos(t, toc, f0, f1, f2, crs, deln, M0, cuc, e, cus, sqrtA, toe, cic, OMG0, cis, i0, crc, omg, OMGd, idot)
% 输入:
% toe: 星历参考时间: 一套星历的有效期为toe前后4小时
% a0 a1 a2 toc: 卫星时钟校正模型方程中3个参数 toc: 第一数据块参考时间, 被用作时钟校正模型中时间参考点
% 卫星星历参数(16个):
% sqrtA: 卫星轨道长半轴的平方根
% es: 轨道偏心率
% i0: toe时刻轨道倾角
% OMEGA0: 周内时等于0时刻的轨道升交点赤经
% omega: 轨道近地角距
% M0: toe时刻的平近点角
% Delta_n 平均运动角速度校正值
% iDOT 轨道倾角对时间的变化率
% OMEGA_DOT 轨道升交点赤经对时间的变化率
% Cuc: 升交点角距余弦和校正振幅
% Cus: 升交点角距正弦和校正振幅
% Cic: 轨道倾角余弦和校正振幅
% Cis: 轨道倾角正弦和校正振幅
%输出:
% X, Y, Z ECEF下卫星位置
% sv_dt 卫星时钟偏差
%变量:
%摄动改正后的升交距角uk、卫星矢径rk和轨道倾角ik
%卫星在轨道面坐标系中的坐标x,y
%发射时刻升交点的经度L
%以下为计算代码
%1.计算卫星运行的平均角速度
n0=sqrt(3.986005E+14)/(sqrtA.^3);
n=n0+deln;
%2.计算信号发射时卫星的平近点角
sv_dt = f0+f1*(t-toc)+f2*(t-toc).^2;%t为未做钟差改正的观测时刻
t = t - 0;%更新t为做钟差改正后的值
tk = t-toe;%归化时间
if tk>302400
tk=tk-604800;
elseif tk<-302400
tk=tk+604800;
else
tk=tk+0;
end
Mk=M0+n*tk;
%3.计算偏近点角(迭代)
%E=M+e*sin(E);
ed(1)=Mk;
for i=1:3
ed(i+1)=Mk+e*sin(ed(i));
end
Ek=ed(end);
%4.计算真近点角
Vk=atan2(sqrt(1-e.^2)*sin(Ek),(cos(Ek)-e));
%5.计算升交距角(未经改进时)
u=omg+Vk;
%6.计算摄动改正项
deltau=cuc*cos(2*u)+cus*sin(2*u);
deltar=crc*cos(2*u)+crs*sin(2*u);
deltai=cic*cos(2*u)+cis*sin(2*u);
%7.计算摄动改正后的升交距角uk、卫星矢径rk和轨道倾角ik
uk=u+deltau;
rk=(sqrtA.^2)*(1-e*cos(Ek))+deltar;
ik=i0+deltai+idot*tk;
%8.计算卫星在轨道面坐标系中的坐标
x=rk*cos(uk);
y=rk*sin(uk);
%9.计算发射时刻升交点的经度67
L=OMG0+(OMGd-7.2921151467e-5)*tk-7.2921151467e-5*toe;
%10.计算卫星在地固坐标系下坐标
XYZ(1)=x*cos(L)-y*cos(ik)*sin(L);
XYZ(2)=x*sin(L)+y*cos(ik)*cos(L);
XYZ(3)=y*sin(ik);
XYZ = XYZ';
end

View File

@ -0,0 +1,8 @@
function SP = ch_sv_pos_rotate(SP, tau)
%% 计算经过地球自转改正后的卫星位置
% Earth's rotation rate
% SP: 卫星位置
omega_e = 7.2921151467e-5; %(rad/sec)
theta = omega_e * tau;
SP = [cos(theta) sin(theta) 0; -sin(theta) cos(theta) 0; 0 0 1]*SP;
end

View File

@ -0,0 +1,19 @@
%*********************************************************************
%
% This function takes a string and checks if it < 80 characters long.
% If so the string is "padded" with zeros until it is 80 characters
% long. Useful when reading RINEX files.
%
%*********************************************************************
function [ current_line ] = check_line_length(current_line)
if length(current_line) < 80
add_spaces = 80 - length(current_line);
for j = 1 : add_spaces
current_line = [ current_line , '0' ];
end
end
% Check if there are any blanks in the data and put a zero there.
current_line = strrep(current_line,' ', '0');

118
lib/gnss/fepoch_0.m Normal file
View File

@ -0,0 +1,118 @@
function [time, dt, sats, eof] = fepoch_0(fid)
% FEPOCH_0 Finds the next epoch in an opened RINEX file with
% identification fid. From the epoch line is produced
% time (in seconds of week), number of sv.s, and a mark
% about end of file. Only observations with epoch flag 0
% are delt with.
%Kai Borre 09-14-96; revised 03-22-97; revised Sept 4, 2001
%Copyright (c) by Kai Borre
%$Revision: 1.0 $ $Date: 1997/09/22 $
%fide = fopen(fid,'rt');
% 该函数只返回o文件中一个历元的以下参数:
% time周内秒
% dt接收机钟差该参数为可选项不一定所有的o文件中都有
% sats当前历元所观测到的卫星
% eof是否到文件末尾
global sat_index;
time = 0;
dt = 0;
sats = [];
NoSv = 0;
eof = 0;
%循环读取o文件的每一行
while 1
lin = fgets(fid); % earlier fgetl 先读一行
if (feof(fid) == 1); % 若到文件末尾,则结束
eof = 1;
break
end;
% 跳过空行
if length(lin) <= 1
continue;
end
% answer = findstr(lin,'COMMENT'); % 判断该行中是否有字符串“COMMENT”
%
% if ~isempty(answer); % 若有“COMMENT“则继续读下一行
% lin = fgetl(fid);
% end;
% 如果该行数据第29个字符不为00代表改历元正常
% 或者总长度只有29也就是没有后面的卫星PRN数据
% 则结束。
% if ((strcmp(lin(29),'0') == 0) & (size(deblank(lin),2) == 29))
% eof = 1;
% break
% end; % We only want type 0 data
% 如果该行第二个字符是1或者第29个字符是0则说明
% 这一行是某一历元的开始行接下来就可以从该行中提取时间、PRN等参数
if ((strcmp(lin(2),'1') == 1) & (strcmp(lin(29),'0') == 1))
ll = length(lin)-2;
if ll > 60, ll = 60; end;
linp = lin(1:ll);
%fprintf('%60s\n',linp);
%使用strtok函数获得间隔符前面的字符串
% 首先是获得当前时间
[nian, lin] = strtok(lin);
% year;
[month, lin] = strtok(lin);
% month;
[day, lin] = strtok(lin);
% day;
[hour, lin] = strtok(lin);
% hour
[minute, lin] = strtok(lin);
% minute
[second, lin] = strtok(lin);
% second
[OK_flag, lin] = strtok(lin);
% OK_flag就是第29个字符如果是0则该历元正常
%将时间转成数值型然后再计算出GPS周和周内秒
h = str2num(hour)+str2num(minute)/60+str2num(second)/3600;
jd = julday(str2num(nian)+2000, str2num(month), str2num(day), h);
[week, sec_of_week] = gps_time(jd);
time = sec_of_week;
%获得该历元卫星数
[NoSv, lin] = strtok(lin,'G');
%储存该历元每颗卫星的PRN
for k = 1:str2num(NoSv)
[sat, lin] = strtok(lin,'G');
prn(k) = str2num(sat);
end
% prn是1行NoSv列的矩阵sats是其转置
sats = prn(:);
% 接收及钟差有的o文件中没有该参数
dT = strtok(lin);
if isempty(dT) == 0 %如果dT不为0则将其记录
dt = str2num(dT);
end
break % 跳出while循环
end
end;
% datee=[str2num(nian) str2num(month) str2num(day) str2num(hour) str2num(minute) str2num(second)];
%%%%%%%% end fepoch_0.m %%%%%%%%%

16
lib/gnss/get_eph.m Normal file
View File

@ -0,0 +1,16 @@
function eph = get_eph(ephemeridesfile)
%GET_EPH The ephemerides contained in ephemeridesfile
% are reshaped into a matrix with 21 rows and
% as many columns as there are ephemerides.
% Typical call eph = get_eph('rinex_n.dat')
%Kai Borre 10-10-96
%Copyright (c) by Kai Borre
%$Revision: 1.0 $ $Date: 1997/09/26 $
fide = fopen(ephemeridesfile);
[eph, count] = fread(fide, Inf, 'double');
noeph = count/23;
eph = reshape(eph, 23, noeph); % ephΪһ¸ö23ÐÐnoephÁеľØÕó
%%%%%%%% end get_eph.m %%%%%%%%%%%%%%%%%%%%%

105
lib/gnss/iono_correction.m Normal file
View File

@ -0,0 +1,105 @@
%This Function approximate Ionospheric Group Delay 这个函数近似电离层群延迟
%CopyRight By Moein Mehrtash
%************************************************************************
% Written by Moein Mehrtash, Concordia University, 3/21/2008 *
% Email: moeinmehrtash@yahoo.com *
%************************************************************************
% ***********************************************************************
% Function for computing an Ionospheric range correction for the *
% GPS L1 frequency from the parameters broadcasted in the GPS *
% Navigation Message.功能:根据GPS导航信息中广播的参数计算电离层距离修正的GPS L1频率。 *
% ==================================================================
% References: *
% Klobuchar, J.A., (1996) "Ionosphercic Effects on GPS", in *
% Parkinson, Spilker (ed), "Global Positioning System Theory and *
% Applications, pp.513-514. *
% ICD-GPS-200, Rev. C, (1997), pp. 125-128 *
% NATO, (1991), "Technical Characteristics of the NAVSTAR GPS", *
% pp. A-6-31 - A-6-33 *
% ==================================================================
% Input : *
% Pos_Rcv : XYZ position of reciever (Meter) *
% Pos_SV : XYZ matrix position of GPS satellites (Meter) *
% GPS_Time : Time of Week (sec) *
% Alfa(4) : The coefficients of a cubic equation *
% representing the amplitude of the vertical *
% dalay (4 coefficients - 8 bits each) 表示垂直dalay振幅的三次方程的系数(4个系数每个8位) *
% Beta(4) : The coefficients of a cubic equation *
% representing the period of the model *
% (4 coefficients - 8 bits each) 表示模型周期的三次方程的系数(4个系数-各8位) *
% Output: *
% Delta_I : Ionospheric slant range correction for *
% the L1 frequency电离层L1频率的倾斜距离校正(Sec) *
% ==================================================================
function [ delay ]=iono_correction(RP, SP, alpha, beta, gpst)
% RP: 接收机位置
% SP: 卫星位置
% Alpha Beta 电离层校准参数
% GPS Time
% delay 单位为m
if norm(RP) < 1
delay = 0;
return;
end
[lat, lon, ~] = ch_ECEF2LLA(RP);
lat = lat/pi;
lon =lon/pi; % semicircles unit Lattitdue and Longitude
[el, az] = satellite_az_el(SP, RP);
E = az/pi; %SemiCircle Elevation
A = el; %SemiCircle Azimoth
% Calculate the Earth-Centered angle, Psi
Psi = 0.0137/(E+.11)-0.022; %SemiCircle
%Compute the Subionospheric lattitude, Phi_L
Phi_L = lat+Psi*cos(A); %SemiCircle
if Phi_L>0.416
Phi_L=0.416;
elseif Phi_L<-0.416
Phi_L=-0.416;
end
%Compute the subionospheric longitude, Lambda_L
Lambda_L = lon+(Psi * sin(A)/cos(Phi_L*pi)); %SemiCircle
%Find the geomagnetic lattitude ,Phi_m, of the subionospheric location
%looking toward each GPS satellite:
Phi_m = Phi_L+0.064*cos((Lambda_L-1.617)*pi);
%Find the Local Time ,t, at the subionospheric point
t=4.32*10^4*Lambda_L+gpst; %GPS_Time(Sec)
t= mod(t,86400);
if t>86400
t = t-86400;
elseif t<0
t=t+86400;
end
%Convert Slant time delay, Compute the Slant Factor,F
F=1+16*(.53-E)^3;
%Compute the ionospheric time delay T_iono by first computing x
Per=beta(1)+beta(2)*Phi_m+beta(3)*Phi_m^2+beta(4)*Phi_m^3;
if Per <72000 %Period
Per=72000;
end
x=2*pi*(t-50400)/Per; %Rad
AMP=alpha(1)+alpha(2)*Phi_m+alpha(3)*Phi_m^2+alpha(4)*Phi_m^3;
if AMP<0
AMP=0;
end
if abs(x)>1.57
T_iono=F*5*10^(-9);
else
T_iono=F*(5*10^(-9)+AMP*(1-x^2/2+x^4/24));
end
delay = T_iono*299792458;

91
lib/gnss/parsef.m Normal file
View File

@ -0,0 +1,91 @@
function varargout = parsef(input, format)
%parsef parse string value using FORTRAN formatting codes
% [val1,val2,...valn] = parsef(input, format)
% input is string input value
% format is cell array of format codes
global input_
input_ = input;
varargout = getvals(1, format, 1);
clear global input_
return
% this function does the work. you probably don't want to go here
function [output, idx] = getvals(idx, format, reps)
global input_
count = 1;
output = {};
for k = 1:reps
odx = 1;
for i = 1:length(format)
fmt = format{i};
switch class(fmt)
case 'double'
count = fmt;
case 'char'
type = fmt(1);
if type == 'X'
idx = idx+count;
else
[len,cnt] = sscanf(fmt,'%*c%d',1);
if cnt ~= 1
error(['Invalid format specifier: ''',fmt,'''']);
end
switch type
case {'I','i'}
for j = 1:count
[val,cnt] = sscanf(input_(idx:min(idx+len-1,end)),'%d',1);
if cnt == 1
output{odx}(j,k) = val;
else
output{odx}(j,k) = NaN;
end
idx = idx+len;
end
case {'F','f'}
for j = 1:count
[val,cnt] = sscanf(input_(idx:min(idx+len-1,end)),'%f',1);
if cnt == 1
output{odx}(j,k) = val;
else
output{odx}(j,k) = NaN;
end
idx = idx+len;
end
case {'E','D','G'}
for j = 1:count
[val,cnt] = sscanf(input_(idx:min(idx+len-1,end)),'%f%*1[DdEe]%f',2);
if cnt == 2
output{odx}(j,k) = val(1) * 10^val(2); %#ok<AGROW>
elseif cnt == 1
output{odx}(j,k) = val;
else
output{odx}(j,k) = NaN;
end
idx = idx+len;
end
case 'A'
for j = 1:count
output{odx}{j,k} = input_(idx:min(idx+len-1,end));
idx = idx+len;
end
otherwise
error(['Invalid format specifier: ''',fmt,'''']);
end
odx = odx+1;
end
count = 1;
case 'cell'
[val, idx] = getvals(idx, fmt, count);
if length(val) == 1
output(odx) = val;
else
output{odx} = val;
end
odx = odx+1;
count = 1;
end
end
end
return

95
lib/gnss/read_rinex_nav.m Normal file
View File

@ -0,0 +1,95 @@
% Lucas Ward
% ASEN 5090
% Read the RINEX navigation file. The header is skipped because
% information in it (a0, a1, iono alpha and beta parameters) is not
% currently needed for orbit computation. This can be easily amended to
% include navigation header information by adding lines in the 'while' loop
% where the header is currently skipped.
%
% Input: - filename - enter the filename to be read. If filename
% exists, the orbit will be calculated.
%
% Output: - ephemeris - Output is a matrix with rows for each PRN and
% columns as follows:
%
% col 1: PRN ....... satellite PRN
% col 2: M0 ....... mean anomaly at reference time
% col 3: delta_n ..... mean motion difference
% col 4: e ....... eccentricity
% col 5: sqrt(A) ..... where A is semimajor axis
% col 6: OMEGA ....... LoAN at weekly epoch
% col 7: i0 ....... inclination at reference time
% col 8: omega ....... argument of perigee
% col 9: OMEGA_dot ... rate of right ascension
% col 10: i_dot ....... rate of inclination angle
% col 11: Cuc ....... cosine term, arg. of latitude
% col 12: Cus ....... sine term, arg. of latitude
% col 13: Crc ....... cosine term, radius
% col 14: Crs ....... sine term, radius
% col 15: Cic ....... cosine term, inclination
% col 16: Cis ....... sine term, inclination
% col 17: toe ....... time of ephemeris
% col 18: IODE ....... Issue of Data Ephemeris
% col 19: GPS_wk ....... GPS week
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ephemeris = read_rinex_nav( filename )
fid = fopen(filename);
if fid == -1
errordlg(['The file ''' filename ''' does not exist.']);
return;
end
% skip through header
end_of_header = 0;
while end_of_header == 0
current_line = fgetl(fid);
if strfind(current_line,'END OF HEADER')
end_of_header=1;
end
end
j = 0;
while feof(fid) ~= 1
j = j+1;
current_line = fgetl(fid);
% parse epoch line (ignores SV clock bias, drift, and drift rate)
[PRN, Y, M, D, H, min, sec,af0,af1,af2] = parsef(current_line, {'I2' 'I3' 'I3' 'I3' 'I3' 'I3' ...
'F5.1','D19.12','D19.12','D19.12'});
% Broadcast orbit line 1
current_line = fgetl(fid);
[IODE Crs delta_n M0] = parsef(current_line, {'D22.12' 'D19.12' 'D19.12' 'D19.12'});
% Broadcast orbit line 2
current_line = fgetl(fid);
[Cuc e Cus sqrtA] = parsef(current_line, {'D22.12' 'D19.12' 'D19.12' 'D19.12'});
% Broadcast orbit line 3
current_line = fgetl(fid);
[toe Cic OMEGA Cis] = parsef(current_line, {'D22.12' 'D19.12' 'D19.12' 'D19.12' 'D19.12'});
% Broadcast orbit line 4
current_line = fgetl(fid);
[i0 Crc omega OMEGA_dot] = parsef(current_line, {'D22.12' 'D19.12' 'D19.12' 'D19.12' 'D19.12'});
% Broadcast orbit line 5
current_line = fgetl(fid);
[i_dot L2_codes GPS_wk L2_dataflag ] = parsef(current_line, {'D22.12' 'D19.12' 'D19.12' 'D19.12' 'D19.12'});
% Broadcast orbit line 6
current_line = fgetl(fid);
[SV_acc SV_health TGD IODC] = parsef(current_line, {'D22.12' 'D19.12' 'D19.12' 'D19.12' 'D19.12'});
% Broadcast orbit line 7
current_line = fgetl(fid);
[msg_trans_t fit_int ] = parsef(current_line, {'D22.12' 'D19.12' 'D19.12'});
[ gps_week, toc ] = UTC2GPST(Y, M, D, H, min, sec);
ephemeris(j,:) = [PRN, M0, delta_n, e, sqrtA, OMEGA, i0, omega, OMEGA_dot, i_dot, Cuc, Cus, Crc, Crs, Cic, Cis, toe, IODE, GPS_wk,toc,af0,af1,af2,TGD];
end

216
lib/gnss/read_rinex_obs.m Normal file
View File

@ -0,0 +1,216 @@
function [rinex,rec_xyz] = read_rinex_obs(fname,nlines)
%*******************************************************
% function [ rinex ] = read_rinex_obs(fname)
%
% DESCRIPTION:
% This function reads a RINEX format GPS data
% file and returns the data in an array.
%
% ARGUMENTS:
% fname (str) - RINEX file
%
% OUTPUT:
% rinex - rinex data (v3 struct)
%
% CALLED BY:
% process_rinex.m
%
% FUNCTIONS CALLED:
% read_rinex_header.m
%
% MODIFICATIONS:
% XX-XX-03 : Jan Weiss - Original
% 03-14-06 : Jan Weiss - Cleanup
% : See SVN log for further modifications
% 10-26-06 : simplified for class positioning project & allows for
% limited number of lines read (PA)
%
% Colorado Center for Astrodynamics Research
% Copyright 2006 University of Colorado, Boulder
%*******************************************************
if (nargin < 2)
nlines = 1e6;
end
% Initialize variables
rinex_data = [];
line_count = 1;
% Read header
[ fid, rec_xyz, observables ] = read_rinex_header(fname);
num_obs = length(observables);
% Get the first line of the observations.
current_line = fgetl(fid);
% If not at the end of the file, search for the desired information.
while current_line ~= -1 & line_count < nlines
% Get the time for this data epoch.
current_time = [ str2num(current_line(2:3)) ; str2num(current_line(5:6)) ; ...
str2num(current_line(8:9)) ; str2num(current_line(11:12)) ; ...
str2num(current_line(14:15)) ; str2num(current_line(17:27)) ]';
% How many SV's are there?
current_num_sv = str2num(current_line(30:32));
% Figure out which PRN's there are.
for ii=1:current_num_sv
current_prn(ii) = str2num(current_line(31 + 3*ii : 32 + 3*ii));
end
% Get the data for all SV's in this epoch.
for ii=1:current_num_sv
% Get the next line.
current_line = fgetl(fid);
line_count = line_count + 1;
% Check the length of the line and pad it with zeros to
% make sure it is 80 characters long.
current_line = check_rinex_line_length(current_line);
% Get the observables on this line.
current_obs = [ str2num(current_line(1:14)) ; str2num(current_line(17:30)) ; ...
str2num(current_line(33:46)) ; str2num(current_line(49:62)) ; str2num(current_line(65:78)) ];
% If there are > 5 observables, read another line to get the rest of the observables for this SV.
if num_obs > 5
% Get the next line.
current_line = fgetl(fid);
line_count = line_count + 1;
% Check the length of the line and pad it with zeros to
% make sure it is 80 characters long.
current_line = check_rinex_line_length(current_line);
% Append the data in this line to the data from previous line.
current_obs = [ current_obs ; str2num(current_line(1:14)) ; ...
str2num(current_line(17:30)) ; str2num(current_line(33:46)) ; ...
str2num(current_line(49:62)) ; str2num(current_line(65:78)) ];
end % if num_obs > 5
% Format the data for this PRN as Date/Time, PRN, Observations.
current_data = [ current_time , current_prn(ii) , current_obs' ];
% Keep only data for the specified PRNs
if nargin == 3 & PRN_list & isempty(find(PRN_list == current_prn(ii)))
continue
end
%Append to the master rinex data file.
rinex_data = [ rinex_data ; current_data ];
end % for ii=1:current_num_sv
% Get the next line.
current_line = fgetl(fid);
line_count = line_count + 1;
end % while current_line ~= -1
% Convert time format
[ gpswk, gpssec ] = cal2gpstime(rinex_data(:,1:6));
rinex.data = [ gpswk gpssec rinex_data(:, 7:end) ];
% Define columns
rinex = define_cols(rinex, observables);
% Convert CP to meters
rinex = convert_rinex_CP(rinex);
% =========================================================================
function rinex = define_cols(rinex, observables)
% Defaults
rinex.col.WEEK = 1;
rinex.col.TOW = 2;
rinex.col.PRN = 3;
col_offset = 3;
for ii=1:length(observables)
switch observables{ii}
case {'L1'}
rinex.col.L1 = ii + col_offset;
case {'L2'}
rinex.col.L2 = ii + col_offset;
case {'P1'}
rinex.col.P1 = ii + col_offset;
case {'P2'}
rinex.col.P2 = ii + col_offset;
case {'C1'}
rinex.col.C1 = ii + col_offset;
case {'D1'}
rinex.col.D1 = ii + col_offset;
case {'S1'}
rinex.col.S1 = ii + col_offset;
case {'S2'}
rinex.col.S2 = ii + col_offset;
end % switch
end % for ii=1:length(observables)
% =========================================================================
function [ rinex ] = convert_rinex_CP(rinex)
set_constants;
if rinex.col.L1 ~= 0
rinex.data(:, rinex.col.L1) = rinex.data(:, rinex.col.L1) * LAMBDA_L1;
end
% =========================================================================
function [ fid, rec_xyz, observables ] = read_rinex_header( file_name )
% Initialize the observables variable.
observables={};
% Assign a file ID and open the given header file.
fid=fopen(file_name);
% If the file does not exist, scream bloody murder!
if fid == -1
display('Error! Header file does not exist.');
else
% Set up a flag for when the header file is done.
end_of_header=0;
% Get the first line of the file.
current_line = fgetl(fid);
% If not at the end of the file, search for the desired information.
while end_of_header ~= 1
% Search for the approximate receiver location line.
if strfind(current_line,'APPROX POSITION XYZ')
% Read xyz coordinates into a matrix.
[rec_xyz] = sscanf(current_line,'%f');
end
% Search for the number/types of observables line.
if strfind(current_line,'# / TYPES OF OBSERV')
% Read the non-white space characters into a temp variable.
[observables_temp] = sscanf(current_line,'%s');
% Read the number of observables space and then create
% a matrix containing the actual observables.
for ii = 1:str2num(observables_temp(1))
observables{ii} = observables_temp( 2*ii : 2*ii+1 );
end
end
% Get the next line of the header file.
current_line = fgetl(fid);
%Check if this line is at the end of the header file.
if strfind(current_line,'END OF HEADER')
end_of_header=1;
end
end
end

204
lib/gnss/rinexe.m Normal file
View File

@ -0,0 +1,204 @@
function iono = rinexe(ephemerisfile, outputfile)
%RINEXE Reads a RINEX Navigation Message file and
% reformats the data into a matrix with 23s
% rows and a column for each satellite.
% The matrix is stored in outputfile
%Typical call: rinexe('pta.96n','pta.nav')
%Kai Borre 04-18-96
%Copyright (c) by Kai Borre
%$Revision: 1.0 $ $Date: 1997/09/24 $
% Units are either seconds, meters, or radians
fide = fopen(ephemerisfile);
head_lines = 0;
iono = zeros(8,1);
%read the header
header_end = [];
while (isempty(header_end))
head_lines = head_lines+1;
%read the line and search the ionosphere labels
lin = fgetl(fide);
vers_found = ~isempty(strfind(lin,'RINEX VERSION / TYPE'));
iono_found = (~isempty(strfind(lin,'ION ALPHA')) || ~isempty(strfind(lin,'IONOSPHERIC CORR')));
%if the ionosphere parameters label was found
if (vers_found)
version = str2num(lin(1:9));
end
%if the ionosphere parameters label was found
if (iono_found)
%change flag
% ioparam = 1;
%save the 8 ionosphere parameters
data = textscan(lin(5:end),'%f%f%f%f%*[^\n]');
if ~isempty(data(4))
iono(1) = data{1};
iono(2) = data{2};
iono(3) = data{3};
iono(4) = data{4};
lin = [];
while isempty(lin)
lin = fgetl(fide);
end
data = textscan(lin(5:end),'%f%f%f%f%*[^\n]');
if ~isempty(data(4))
iono(5) = data{1};
iono(6) = data{2};
iono(7) = data{3};
iono(8) = data{4};
else
iono = zeros(8,1);
end
end
end
header_end = strfind(lin,'END OF HEADER');
end
head_lines = head_lines + 1;
noeph = -1; %初始化星历数目
while 1
noeph = noeph+1;
line = fgetl(fide);
if line == -1, break; end
end;
noeph = noeph/8 % 卫星星历数 = 去掉文件头之后的文件行数 / 8
% 每颗卫星星历行数是8行
% 下面两行作用是回到文件头结束的地方
frewind(fide);
for i = 1:head_lines, line = fgetl(fide); end;
% Set aside memory for the input
% 为星历中每个参数产生1行noeph列的0矩阵
% 每一列对应该星历文件中的一个PRN
noeph = fix(noeph);
svprn = zeros(1,noeph);
weekno = zeros(1,noeph);
t0c = zeros(1,noeph);
tgd = zeros(1,noeph);
aodc = zeros(1,noeph);
toe = zeros(1,noeph);
af2 = zeros(1,noeph);
af1 = zeros(1,noeph);
af0 = zeros(1,noeph);
aode = zeros(1,noeph);
deltan = zeros(1,noeph);
M0 = zeros(1,noeph);
ecc = zeros(1,noeph);
roota = zeros(1,noeph);
toe = zeros(1,noeph);
cic = zeros(1,noeph);
crc = zeros(1,noeph);
cis = zeros(1,noeph);
crs = zeros(1,noeph);
cuc = zeros(1,noeph);
cus = zeros(1,noeph);
Omega0 = zeros(1,noeph);
omega = zeros(1,noeph);
i0 = zeros(1,noeph);
Omegadot = zeros(1,noeph);
idot = zeros(1,noeph);
accuracy = zeros(1,noeph);
health = zeros(1,noeph);
fit_interval = zeros(1,noeph);
% 将所有卫星的星历参数存入上面定义的矩阵中
for i = 1:noeph
line = fgetl(fide); % 从该组星历中第一颗卫星的星历的第一行开始
svprn(i) = str2num(line(1:2)); % 存入卫星PRN
year = line(3:6);
month = line(7:9);
day = line(10:12);
hour = line(13:15);
minute = line(16:18);
second = line(19:22);
af0(i) = str2num(line(23:41));
af1(i) = str2num(line(42:60));
af2(i) = str2num(line(61:79));
line = fgetl(fide); % 读下一行
IODE = line(4:22);
crs(i) = str2num(line(23:41));
deltan(i) = str2num(line(42:60));
M0(i) = str2num(line(61:79));
line = fgetl(fide); % 读下一行
cuc(i) = str2num(line(4:22));
ecc(i) = str2num(line(23:41));
cus(i) = str2num(line(42:60));
roota(i) = str2num(line(61:79));
line=fgetl(fide); % 读下一行
toe(i) = str2num(line(4:22));
cic(i) = str2num(line(23:41));
Omega0(i) = str2num(line(42:60));
cis(i) = str2num(line(61:79));
line = fgetl(fide); % 读下一行
i0(i) = str2num(line(4:22));
crc(i) = str2num(line(23:41));
omega(i) = str2num(line(42:60));
Omegadot(i) = str2num(line(61:79));
line = fgetl(fide); % 读下一行
idot(i) = str2num(line(4:22));
codes = str2num(line(23:41));
weekno = str2num(line(42:60));
L2flag = str2num(line(61:79));
line = fgetl(fide); % 读下一行
svaccur = str2num(line(4:22));
svhealth(i) = str2num(line(23:41));
tgd(i) = str2num(line(42:60));
iodc = line(61:79);
line = fgetl(fide); % 读下一行
if length(line)>= 41
tom(i) = str2num(line(4:22));
fit_interval(i) = str2num(line(23:41));
else
tom(i) = str2num(line(4:22));
fit_interval(i) = 0;
end
% spare = line(42:60);
% spare = line(61:79);
end
status = fclose(fide)
% Description of variable eph.
% 把上面存好的各参数再转存到一个大矩阵“eph”中该矩阵每行代表一种参数
% 每列代表一颗卫星
eph(1,:) = svprn;
eph(2,:) = af2;
eph(3,:) = M0;
eph(4,:) = roota;
eph(5,:) = deltan;
eph(6,:) = ecc;
eph(7,:) = omega;
eph(8,:) = cuc;
eph(9,:) = cus;
eph(10,:) = crc;
eph(11,:) = crs;
eph(12,:) = i0;
eph(13,:) = idot;
eph(14,:) = cic;
eph(15,:) = cis;
eph(16,:) = Omega0;
eph(17,:) = Omegadot;
eph(18,:) = toe;
eph(19,:) = af0;
eph(20,:) = af1;
eph(21,:) = toe;
eph(22,:) = fit_interval;
eph(23,:) = svhealth;
fidu = fopen(outputfile,'w');
count = fwrite(fidu,[eph],'double');
fclose all
%%%%%%%%% end rinexe.m %%%%%%%%%


View File

@ -0,0 +1,25 @@
function [az, el] = satellite_az_el(SP, RP)
%% 计算卫星俯仰角(el), 和方位角(az)
% get_satellite_az_el: computes the satellite azimuth and elevation
% given the position of the user and the satellite in ECEF
% : SP: 卫星位置ECEF
% RP: 用户位置ECEF
% Output Args:
% az: azimuth: 方位角
% el: elevation 俯仰角
% 参考 GPS 原理及接收机设计 谢刚
[lat, lon, ~] = ch_ECEF2LLA(RP);
C_ECEF2ENU = [-sin(lon) cos(lon) 0; -sin(lat)*cos(lon) -sin(lat)*sin(lon) cos(lat); cos(lat)*cos(lon) cos(lat)*sin(lon) sin(lat)];
enu = C_ECEF2ENU*[SP - RP];
% nroamlized line of silght vector
enu = enu / norm(enu);
az = atan2(enu(1), enu(2));
el = asin(enu(3)/norm(enu));
end

92
lib/gnss/set_constants.m Normal file
View File

@ -0,0 +1,92 @@
%*******************************************************
%
% DESCRIPTION:
% This script contains many useful constants for GPS
% and related work. It should be kept in only
% one place so that updates are immediately available
% to all other scripts/functions.
%
% ARGUMENTS:
% None, just call this script to place
% the constants in your workspace.
%
% OUTPUT:
% Variables in your current workspace.
%
% CALLED BY:
% Many other codes.
%
% FUNCTIONS CALLED:
% None.
%
% MODIFICATIONS:
% XX-XX-02 : Jan Weiss - Original
% 07-25-04 : Jan Weiss - updated header.
% 10-19-04 : Jan Weiss - Cleanup and added
% some conversion factors.
% : See SVN log for further updates.
%
% Colorado Center for Astrodynamics Research
% Copyright 2005 University of Colorado, Boulder
%*******************************************************
% GENERAL CONSTANTS
% =========================================================================
c = 299792458; %----> Speed of light (meters/s).
Re = 6378137 ; %----> Earth Radius (meters)
% =========================================================================
% CONVERSION FACTORS
% =========================================================================
Hz2MHz = 1E-6;
MHz2Hz = 1E6;
s2ns = 1E9;
ns2s = 1E-9;
s2micros = 1E6;
micros2s = 1E-6;
s2ms = 1E3;
ms2s = 1E-3;
dtr = pi / 180;
rtd = 180 / pi;
m2cm = 100;
cm2m = 1 / 100;
m2mm = 1000;
mm2m = 1 / 1000;
ft2m = 0.3048; % Source: http://www.nodc.noaa.gov/dsdt/ucg/
m2ft = 1 / 0.3048;
ns2m = c * ns2s; % Converts time in nano-sec to distance,
% assuming the speed of light.
% =========================================================================
% GNSS SPECIFIC CONSTANTS
% =========================================================================
L1 = 1575.42e6; %----> Freqs in Hz.
L2 = 1227.60e6;
L5 = 1176.45e6;
L1MHz = 1575.42; %----> Freqs in MHz.
L2MHz = 1227.60;
L5MHz = 1176.45;
L1GHz = 1.57542; %----> Freqs in GHz.
L2GHz = 1.22760;
L5GHz = 1.17645;
LAMBDA_L1 = c / L1; %----> Wavelengths in meters.
LAMBDA_L2 = c / L2;
LAMBDA_L5 = c / L5;
CA_CODE_RATE = 1.023e6; %----> C/A and P code chipping rate in chips/s.
P_CODE_RATE = 10.23e6;
CA_CHIP_PERIOD = 1 / CA_CODE_RATE; %----> C/A & P code chip periods in s.
P_CHIP_PERIOD = 1 / P_CODE_RATE;
CA_CHIP_LENGTH = c / CA_CODE_RATE; %----> C/A & P code chip lengths in meters.
P_CHIP_LENGTH = c / P_CODE_RATE;
CA_CODE_LENGTH = 1023; % chips
% =========================================================================

37
lib/gnss/sv_clock_bias.m Normal file
View File

@ -0,0 +1,37 @@
function dtr = sv_clock_bias(t, toc, a0, a1, a2, e, sqrtA, toe, Delta_n, M0)
% 输入:
% a0 a1 a2 toc: 卫星时钟校正模型方程中3个参数 toc: 第一数据块参考时间, 被用作时钟校正模型中时间参考点
% dtr 卫星时钟偏差
dtr = a0+a1*(t-toc)+a2*(t-toc).^2;%t为未做钟差改正的观测时刻
F = -4.442807633e-10;
mu = 3.986005e14;
A = sqrtA^2;
cmm = sqrt(mu/A^3); % computed mean motion
tk = t - toe;
% account for beginning or end of week crossover
if (tk > 302400)
tk = tk-604800;
end
if (tk < -302400)
tk = tk+604800;
end
% apply mean motion correction
n = cmm + Delta_n;
% Mean anomaly
mk = M0 + n*tk;
% solve for eccentric anomaly
Ek = mk;
Ek = mk + e*sin(Ek);
Ek = mk + e*sin(Ek);
Ek = mk + e*sin(Ek);
% dsv 时间为s
dtr = dtr + F*e*sqrtA*sin(Ek);
end

View File

@ -0,0 +1,86 @@
function [corr] = tropo_correction(el, h)
% SYNTAX:
% [corr] = tropo_error_correction(el, h);
%
% INPUT:
% el = satellite elevation (rad)
% h = receiver ellipsoidal height (m)
%
% OUTPUT:
% corr = tropospheric error correction
%
% DESCRIPTION:
% Computation of the pseudorange correction due to tropospheric refraction.
% Saastamoinen algorithm.
%----------------------------------------------------------------------------------------------
% goGPS v0.4.3
%
% Copyright (C) 2009-2014 Mirko Reguzzoni, Eugenio Realini
%
% Portions of code contributed by Laboratorio di Geomatica, Polo Regionale di Como,
% Politecnico di Milano, Italy
%----------------------------------------------------------------------------------------------
global tropo_model
%Saastamoinen model requires positive ellipsoidal height
h(h < 0) = 0;
% If in ATM_model section in config file tropo is set to 0 it does not use tropospheric model
% [ATM_model]
% tropo=0
if (tropo_model == 0)
corr = zeros(size(el));
else
if (h < 5000)
%conversion to radians
el = abs(el);
%Standard atmosphere - Berg, 1948 (Bernese)
%pressure [mbar]
Pr = 1013.25;
%temperature [K]
Tr = 291.15;
%numerical constants for the algorithm [-] [m] [mbar]
Hr = 50.0;
P = Pr * (1-0.0000226*h).^5.225;
T = Tr - 0.0065*h;
H = Hr * exp(-0.0006396*h);
%----------------------------------------------------------------------
%linear interpolation
h_a = [0; 500; 1000; 1500; 2000; 2500; 3000; 4000; 5000];
B_a = [1.156; 1.079; 1.006; 0.938; 0.874; 0.813; 0.757; 0.654; 0.563];
t = zeros(length(T),1);
B = zeros(length(T),1);
for i = 1 : length(T)
d = h_a - h(i);
[dmin, j] = min(abs(d));
if (d(j) > 0)
index = [j-1; j];
else
index = [j; j+1];
end
t(i) = (h(i) - h_a(index(1))) ./ (h_a(index(2)) - h_a(index(1)));
B(i) = (1-t(i))*B_a(index(1)) + t(i)*B_a(index(2));
end
%----------------------------------------------------------------------
e = 0.01 * H .* exp(-37.2465 + 0.213166*T - 0.000256908*T.^2);
%tropospheric error
corr = ((0.002277 ./ sin(el)) .* (P - (B ./ (tan(el)).^2)) + (0.002277 ./ sin(el)) .* (1255./T + 0.05) .* e);
else
corr = zeros(size(el));
end
end

73
lib/madgwick.m Normal file
View File

@ -0,0 +1,73 @@
classdef madgwick < handle
methods (Static = true)
function q = imu(q, Gyroscope, Accelerometer, SamplePeriod, Beta)
% Normalise accelerometer measurement
acc = Accelerometer;
if(norm(acc) == 0), return; end % handle NaN
acc = acc / norm(acc); % normalise magnitude
% Gradient decent algorithm corrective step
F = (quatrotate(q, [0 0 1]) - acc)';
% F = [2*(q(2)*q(4) - q(1)*q(3)) - Accelerometer(1)
% 2*(q(1)*q(2) + q(3)*q(4)) - Accelerometer(2)
% 2*(0.5 - q(2)^2 - q(3)^2) - Accelerometer(3)];
J = [-2*q(3), 2*q(4), -2*q(1), 2*q(2)
2*q(2), 2*q(1), 2*q(4), 2*q(3)
0, -4*q(2), -4*q(3), 0 ];
step = (J'*F);
step = step / norm(step); % normalise step magnitude
% Compute rate of change of quaternion
qd = 0.5 * quatmultiply(q, [0 Gyroscope]) - Beta * step';
% Integrate to yield quaternion
q = q + qd * SamplePeriod;
q = q / norm(q); % normalise quaternion
end
function q = ahrs(q, Gyroscope, Accelerometer, Magnetometer, SamplePeriod, Beta)
% Normalise accelerometer measurement
if(norm(Accelerometer) == 0), return; end % handle NaN
acc = Accelerometer / norm(Accelerometer); % normalise magnitude
% Normalise magnetometer measurement
if(norm(Magnetometer) == 0), return; end % handle NaN
mag = Magnetometer / norm(Magnetometer); % normalise magnitude
% Reference direction of Earth's magnetic feild
h = quatrotate(quatconj(q), mag);
h = [0 h];
b = [0 norm([h(2) h(3)]) 0 h(4)];
% Gradient decent algorithm corrective step
F= (quatrotate(q, [0 0 1]) - acc)';
F = [F; (quatrotate(q, [b(2) 0 b(4)]) - mag)'];
% F = [2*(q(2)*q(4) - q(1)*q(3)) - Accelerometer(1)
% 2*(q(1)*q(2) + q(3)*q(4)) - Accelerometer(2)
% 2*(0.5 - q(2)^2 - q(3)^2) - Accelerometer(3)
% 2*b(2)*(0.5 - q(3)^2 - q(4)^2) + 2*b(4)*(q(2)*q(4) - q(1)*q(3)) - Magnetometer(1)
% 2*b(2)*(q(2)*q(3) - q(1)*q(4)) + 2*b(4)*(q(1)*q(2) + q(3)*q(4)) - Magnetometer(2)
% 2*b(2)*(q(1)*q(3) + q(2)*q(4)) + 2*b(4)*(0.5 - q(2)^2 - q(3)^2) - Magnetometer(3)];
J = [-2*q(3), 2*q(4), -2*q(1), 2*q(2)
2*q(2), 2*q(1), 2*q(4), 2*q(3)
0, -4*q(2), -4*q(3), 0
-2*b(4)*q(3), 2*b(4)*q(4), -4*b(2)*q(3)-2*b(4)*q(1), -4*b(2)*q(4)+2*b(4)*q(2)
-2*b(2)*q(4)+2*b(4)*q(2), 2*b(2)*q(3)+2*b(4)*q(1), 2*b(2)*q(2)+2*b(4)*q(4), -2*b(2)*q(1)+2*b(4)*q(3)
2*b(2)*q(3), 2*b(2)*q(4)-4*b(4)*q(2), 2*b(2)*q(1)-4*b(4)*q(3), 2*b(2)*q(2)];
step = (J'*F);
step = step / norm(step); % normalise step magnitude
% Compute rate of change of quaternion
qd = 0.5 * quatmultiply(q, [0 Gyroscope]) - Beta * step';
% Integrate to yield quaternion
q = q + qd * SamplePeriod;
q = q / norm(q); % normalise quaternion
end
end
end

52
lib/mahony.m Normal file
View File

@ -0,0 +1,52 @@
classdef mahony < handle
methods (Static = true)
function q = imu(q, gyr, acc, dt, Kp)
% 속醵똑데貫뺏
norm_acc = norm(acc);
if((norm_acc) == 0), return; end
acc = acc / norm(acc);
qtmp = att_upt(q, gyr, dt);
v = qmulv(qconj(qtmp), [0 0 1]');
e = cross(acc, v) ;
% 럽웩
gyr = gyr + Kp * e ;
% 생롸
q = qintg(q, gyr, dt);
end
function q = ahrs(q, gyr, acc, mag, dt, Kp)
% 속醵똑셕데貫뺏
if(norm(acc) == 0), return; end % handle NaN
acc = acc / norm(acc); % normalise magnitude
% 늚끝데貫뺏
if(norm(mag) == 0), return; end % handle NaN
mag = mag / norm(mag); % normalise magnitude
% Reference direction of Earth's magnetic feild
h = qmulv(q, mag);
b = [norm([h(1) h(2)]) 0 h(3)]';
% Estimated direction of gravity and magnetic field
w = qmulv(qconj(q), b);
v = qmulv(qconj(q), [0 0 1]');
% Error is sum of cross product between estimated direction and measured direction of fields
e = cross(acc, v) + cross(mag, w);
% Apply feedback terms
gyr = gyr + Kp * e ;
% integate
q = qintg(q, gyr, dt);
end
end
end

23
lib/mat2c.m Normal file
View File

@ -0,0 +1,23 @@
function mat2c(mat)
[r, c] = size(mat);
mn = r*c;
fprintf('{');
if r==1||c==1
for i=1:mn-1
fprintf('%f,', mat(i));
end
fprintf('%f};\n', mat(i+1));
else
for i=1:r-1
for j=1:c
fprintf('%f,', mat(i,j));
end
fprintf('\n');
end
for j=1:c-1
fprintf('%f,', mat(i+1,j));
end
fprintf('%f};\n', mat(i+1,j+1));
end
end

81
lib/multilateration.m Normal file
View File

@ -0,0 +1,81 @@
function pos = multilateration(sv_pos, pos, pr, dim)
%% 最小二乘法多边测距 解算标签位置
% M x N: M:维度 2 OR 3 N:基站个数
% sv_pos: 基站位置 M x N
% pos: M x 1
% pr: 伪距 N x 1
% dim : 2 or 3 : 2: 二维定位 3: 三维定位
B1 = 0.1; % 迭代收敛阈值(位置更新量小于此值时停止)
END_LOOP = 100; % 初始迭代残差(设为较大值)
sv_num = size(sv_pos, 2); % 基站数量
max_retry = 5; % 最大迭代次数
lpos = pos; % 保存上一次的位置,用于迭代失败时回退
% 设置约束如果基站数量少于3则直接退出。
% 因为解算位置需满足以下条件:
% 2D定位至少3个非共线基站。
% 3D定位至少4个非共面基站。
if sv_num < 3
return
end
while (END_LOOP > B1 && max_retry > 0) % 循环条件位置更新量大于阈值B1且未超过最大迭代次数
% 获得当前位置与各个基站的欧氏距离
r = vecnorm(sv_pos - pos);
% 求得H矩阵
H = (sv_pos - pos) ./ r; % 求每个矩阵的单位方向向量, ./是将矩阵中每个对应元素逐个做除法运算
if dim == 2
H = [H [0 0 -1]']; % 如果是2D模式添加虚拟高度约束
end
H =-H'; % 转置并取负H 是伪距对位置的偏导数矩阵(雅可比矩阵),用于线性化非线性问题
% 构建残差向量dp
dp = (pr - r)'; % 伪距测量残差 = 测量值 - 预测值
if dim == 2
dp = [dp; 0]; % 如果是2D模式添加高度残差约束
end
% 迭代用户距离(位置增量)
delta = (H'*H)^(-1)*H'*dp; % 最小二乘解算
%计算残差,以判断残差大小是否可以停止迭代
% 残差就是“测量值减去估计值”的差距,代表你当前估计的误差。
END_LOOP = norm(delta); % 欧几里得范数(向量的长度),表示这次更新的幅度。
%更新位置
pos = pos + delta;
max_retry = max_retry - 1; % 减少剩余迭代次数
%迭代失败 如果到达了最大迭代次数还没有收敛,则说明定位失败
if(max_retry == 0 && END_LOOP > 10)
pos = lpos; % 就会回滚到上一次迭代的结果
return;
end
end
end
%
% % 最小二乘法多边测距
%
% function pos = ch_multilateration(anchor_pos, pos, pr)
%
% pr = pr(1:size(anchor_pos, 2));
%
% b = vecnorm(anchor_pos).^(2) - pr.^(2);
% b = b(1:end-1) - b(end);
% b = b';
%
% A = anchor_pos - anchor_pos(:,end);
% A = A(:,1:end-1)'*2;
%
% pos = (A'*A)^(-1)*A'*b;
%
%
% end

60
lib/nav_equ_local_tan.m Normal file
View File

@ -0,0 +1,60 @@
function [p, v, q] = nav_equ_local_tan(p, v, q ,acc, gyr, dt, gN)
% 惯导解算更新,当地直角坐标系,不考虑地球自转
% p 位置 XYZ 单位 m
% v 速度 XYZ 单位 m/s
% q Qb2n姿态,四元数表示
% acc 比力, 加速度计测量值 单位 (m/s^2),
% gyr 角速度 (rad/s)]
% dt dt (s) 积分间隔如 0.01s
% gn 当地重力向量
old_v = v; % 保存当前速度,用于后续梯形积分
sf = acc; % 比力Specific Force = 加速度计测量值(忽略零偏和噪声)
% 姿态结算
q = att_upt(q, gyr, dt); % 通过陀螺仪数据更新四元数
% 输入当前姿态四元数和陀螺仪角速度,输出更新后的姿态四元数
% 速度解算
sf = qmulv(q, sf); % 将比力从机体系转换到导航系
sf = sf + gN; % 添加重力补偿(导航系下)
v = old_v + dt *sf; % 积分加速度得到速度
% 位置解算
p = p + (old_v + v) *dt/2; % 梯形法积分速度
end
%
%
% function x = ch_nav_equ_local_tan(x ,u, dt, gN)
%
% persistent a_old;
% if isempty(a_old)
% a_old= u(1:3);
% end
%
% old_v = x(4:6);
%
% a_new =u(1:3);
% %sf = sf + 0.5*cross(u(4:6)*dt, sf);
%
% % 姿态结算
% gyr = u(4:6);
% q_old = x(7:10);
% x(7:10) = ch_att_upt(x(7:10), gyr, dt);
% q_new = x(7:10);
%
% % 速度解算
%
% x(4:6) = old_v + ((ch_qmulv(q_new, a_new) + ch_qmulv(q_old, a_old) )/2 + gN) *dt;
%
% % 位置解算
% x(1:3) = x(1:3) + (old_v + x(4:6)) *dt/2;
% a_old = a_new;
% end
%
%

6
lib/ned2enu.m Normal file
View File

@ -0,0 +1,6 @@
function ENU = ned2enu(NED)
end

30
lib/plot/ch_plot_att.m Normal file
View File

@ -0,0 +1,30 @@
function ch_plot_att(att, varargin)
% plot 欧拉角
% att 欧拉角
% 可选参数: units: "rad" 或者 "deg", 默认 rad
defaultUnits = 'rad';
param= inputParser;
addParameter(param,'units',defaultUnits,@isstring);
param.parse(varargin{:});
r = param.Results;
if(~isempty(r.units))
defaultUnits = r.units;
end
plot(att);
title("欧拉角");
legend("X", "Y", "Z");
xlabel("解算次数");
ylabel(defaultUnits);
end

View File

@ -0,0 +1,39 @@
% subplot£ºÊÇ·ñ¿ªÆôsubplot
function ch_plot_gps_imu_pos(varargin)
%% plot imu data
i = 1;
param= inputParser;
param.addOptional('time', []);
param.addOptional('pos', []);
param.addOptional('gnss', []);
param.parse(varargin{:});
r = param.Results;
if(r.time == 0 )
error('no time data');
end
figure;
subplot(2,1,1);
plot(r.gnss(:,2), r.gnss(:,1),'b-');
hold on;
plot(r.gnss(:,2), r.gnss(:,1),'b.');
plot(r.pos(:,2), r.pos(:,1), 'r-');
plot(r.pos(1,1), r.pos(1,2),'ks');
legend('GNSS position estimate','GNSS aided INS trajectory','Start point')
axis equal
hold off;
xlabel('X(m)'); ylabel('Y(m)'); title('Trajectory');
subplot(2,1,2);
hold on;
plot(1:length(r.gnss), -r.gnss(:,3),'b.');
plot(r.time, -r.pos(:,3),'r');
legend('GNSS estimate','GNSS aided INS estimate')
title('Height versus time'); xlabel('Time [s]'); ylabel('Height [m]');
hold off;
end

108
lib/plot/ch_plot_imu.m Normal file
View File

@ -0,0 +1,108 @@
% subplot是否开启subplot
function ch_plot_imu(varargin)
%% plot imu data
i = 1;
param= inputParser;
param.addOptional('time', []);
param.addOptional('acc', []);
param.addOptional('gyr', []);
param.addOptional('mag', []);
param.addOptional('eul', []);
param.addOptional('gb', []); % 加速度零偏
param.addOptional('wb', []); % 陀螺零偏
param.addOptional('phi', []); %失准角
param.addOptional('P_phi', []); %失准角方差
param.addOptional('P_wb', []); %陀螺方差
param.addOptional('P_pos', []); %位置方差
param.addOptional('title', []);
param.addOptional('legend', []);
%然后将输入的参数进行处理,如果有不同于默认值的那就覆盖掉
param.parse(varargin{:});
r = param.Results;
if(r.time == 0 )
error('no time data');
end
i = 1;
figure;
if(~isempty(r.gyr))
subplot(2,2,i);
i = i+1;
interial_display(r.time, r.gyr, {'X', 'Y', 'Z'}, 'Time (s)', 'Angular rate)', 'Gyroscope');
end
if(~isempty(r.acc))
subplot(2,2,i);
i = i+1;
interial_display(r.time, r.acc, {'X', 'Y', 'Z'}, 'Time (s)', 'Acceleration', 'Accelerometer');
end
if(~isempty(r.mag))
subplot(2,2,i);
i = i+1;
interial_display(r.time, r.mag, {'X', 'Y', 'Z'}, 'Time (s)', 'Flux ', 'Magnetometer');
end
if(~isempty(r.eul))
subplot(2,2,i);
i = i+1;
interial_display(r.time, r.eul, {'X', 'Y', 'Z'}, 'Time (s)', 'Angle(deg)', 'Eular Angle');
end
if(~isempty(r.wb))
subplot(2,2,i);
i = i+1;
interial_display(r.time, r.wb, {'X', 'Y', 'Z'}, 'Time (s)', 'Angle(deg)', '陀螺零偏');
end
if(~isempty(r.gb))
subplot(2,2,i);
i = i+1;
interial_display(r.time, r.gb, {'X', 'Y', 'Z'}, 'Time (s)', 'm/s^(2)', '加速度零偏');
end
if(~isempty(r.phi))
subplot(2,2,i);
i = i+1;
interial_display(r.time, r.phi, {'X', 'Y', 'Z'}, 'Time (s)', 'Angle(deg)', '失准角');
end
if(~isempty(r.P_phi))
subplot(2,2,i);
i = i+1;
interial_display(r.time, r.P_phi, {'X', 'Y', 'Z'}, 'Time (s)', '-', 'Phi Var(失准角方差)');
end
if(~isempty(r.P_wb))
subplot(2,2,i);
i = i+1;
interial_display(r.time, r.P_wb, {'X', 'Y', 'Z'}, 'Time (s)', '-', '陀螺零偏方差');
end
if(~isempty(r.P_pos))
subplot(2,2,i);
interial_display(r.time, r.P_pos, {'X', 'Y', 'Z'}, 'Time (s)', '-', '位置方差');
end
% linkaxes(axis, 'x');
end
function interial_display(time, data, legendstr, xlabelstr, ylabelstr, titlestr)
hold on;
plot(time, data(:,1), 'r');
plot(time, data(:,2), 'g');
plot(time, data(:,3), 'b');
legend(legendstr);
xlabel(xlabelstr);
ylabel(ylabelstr);
title(titlestr);
hold off;
end

26
lib/plot/ch_plot_pos2d.m Normal file
View File

@ -0,0 +1,26 @@
function ch_plot_pos2d(pos, varargin)
% pos: 位置: X, Y
defaultUnits = 'm';
param= inputParser;
addParameter(param,'units',defaultUnits,@isstring);
param.parse(varargin{:});
r = param.Results;
if(~isempty(r.units))
defaultUnits = r.units;
end
plot(pos(:,1), pos(:,2));
title("2D位置" + "(" + defaultUnits + ")") ;
xlabel("X"); ylabel("Y");
end

25
lib/plot/ch_plot_pos3d.m Normal file
View File

@ -0,0 +1,25 @@
function ch_plot_pos3d(pos, varargin)
% pos: 位置: X, Y, Z
defaultUnits = 'm';
param= inputParser;
addParameter(param,'units',defaultUnits,@isstring);
param.parse(varargin{:});
r = param.Results;
if(~isempty(r.units))
defaultUnits = r.units;
end
plot(pos(:,1), pos(:,2));
plot3(pos(:,1), pos(:,2), pos(:,3));
title("3D位置" + "(" + defaultUnits + ")") ;
xlabel("X"); ylabel("Y"); zlabel("Z");
end

47
lib/ros_read_bag.m Normal file
View File

@ -0,0 +1,47 @@
%% Example
% path = 'imu3_codeodom_tst.bag';
%
% rosbag info 'imu3_codeodom_tst.bag'
% [imu, eul]= ch_ros_read_bag(path, '/imu_hi226');
% ch_imu_data_plot('acc', imu.acc', 'gyr', imu.gyr', 'eul', eul', 'time', imu.time', 'subplot', 1);
%%
function [imu, eul]= ros_read_bag(path, imu_topic_name)
bag = rosbag(path);
%ÏÔʾbagÐÅÏ¢
% rosbag info 'imu3_codeodom_tst.bag'
% bag = select(bag, 'Time', [bag.StartTime+100 bag.StartTime + 110], 'Topic',imu_topic_name);
bag = select(bag, 'Topic', imu_topic_name);
imu_cell = readMessages(bag);
n = length(imu_cell);
span = bag.EndTime - bag.StartTime ;
imu.time = 0: span / n : span;
imu.time(end) = []; % remove last element
for i = 1:n
imu.gyr(1,i) = imu_cell{i,1}.AngularVelocity.X;
imu.gyr(2,i) = imu_cell{i,1}.AngularVelocity.Y;
imu.gyr(3,i) = imu_cell{i,1}.AngularVelocity.Z;
imu.acc(1,i) = imu_cell{i,1}.LinearAcceleration.X;
imu.acc(2,i) = imu_cell{i,1}.LinearAcceleration.Y;
imu.acc(3,i) = imu_cell{i,1}.LinearAcceleration.Z;
q(1) = imu_cell{i, 1}.Orientation.W;
q(2) = imu_cell{i, 1}.Orientation.X;
q(3) = imu_cell{i, 1}.Orientation.Y;
q(4) = imu_cell{i, 1}.Orientation.Z;
eul(:, i) = q2eul(q);
end
eul = rad2deg(eul);
end

View File

@ -0,0 +1,255 @@
function fig = SixDOFanimation(varargin)
%% Create local variables
% Required arguments
p = varargin{1}; % position of body
R = varargin{2}; % rotation matrix of body
[numSamples dummy] = size(p);
% Default values of optional arguments
SamplePlotFreq = 1;
Trail = 'Off';
LimitRatio = 1;
Position = [];
FullScreen = false;
View = [30 20];
AxisLength = 1;
ShowArrowHead = 'on';
Xlabel = 'X';
Ylabel = 'Y';
Zlabel = 'Z';
Title = '6DOF Animation';
ShowLegend = true;
CreateAVI = false;
AVIfileName = '6DOF Animation';
AVIfileNameEnum = true;
AVIfps = 30;
for i = 3:2:nargin
if strcmp(varargin{i}, 'SamplePlotFreq'), SamplePlotFreq = varargin{i+1};
elseif strcmp(varargin{i}, 'Trail')
Trail = varargin{i+1};
if(~strcmp(Trail, 'Off') && ~strcmp(Trail, 'DotsOnly') && ~strcmp(Trail, 'All'))
error('Invalid argument. Trail must be ''Off'', ''DotsOnly'' or ''All''.');
end
elseif strcmp(varargin{i}, 'LimitRatio'), LimitRatio = varargin{i+1};
elseif strcmp(varargin{i}, 'Position'), Position = varargin{i+1};
elseif strcmp(varargin{i}, 'FullScreen'), FullScreen = varargin{i+1};
elseif strcmp(varargin{i}, 'View'), View = varargin{i+1};
elseif strcmp(varargin{i}, 'AxisLength'), AxisLength = varargin{i+1};
elseif strcmp(varargin{i}, 'ShowArrowHead'), ShowArrowHead = varargin{i+1};
elseif strcmp(varargin{i}, 'Xlabel'), Xlabel = varargin{i+1};
elseif strcmp(varargin{i}, 'Ylabel'), Ylabel = varargin{i+1};
elseif strcmp(varargin{i}, 'Zlabel'), Zlabel = varargin{i+1};
elseif strcmp(varargin{i}, 'Title'), Title = varargin{i+1};
elseif strcmp(varargin{i}, 'ShowLegend'), ShowLegend = varargin{i+1};
elseif strcmp(varargin{i}, 'CreateAVI'), CreateAVI = varargin{i+1};
elseif strcmp(varargin{i}, 'AVIfileName'), AVIfileName = varargin{i+1};
elseif strcmp(varargin{i}, 'AVIfileNameEnum'), AVIfileNameEnum = varargin{i+1};
elseif strcmp(varargin{i}, 'AVIfps'), AVIfps = varargin{i+1};
else error('Invalid argument.');
end
end;
%% Reduce data to samples to plot only
p = p(1:SamplePlotFreq:numSamples, :);
R = R(:, :, 1:SamplePlotFreq:numSamples) * AxisLength;
if(numel(View) > 2)
View = View(1:SamplePlotFreq:numSamples, :);
end
[numPlotSamples dummy] = size(p);
%% Setup AVI file
aviobj = []; % create null object
if(CreateAVI)
fileName = strcat(AVIfileName, '.avi');
if(exist(fileName, 'file'))
if(AVIfileNameEnum) % if file name exists and enum enabled
i = 0;
while(exist(fileName, 'file')) % find un-used file name by appending enum
fileName = strcat(AVIfileName, sprintf('%i', i), '.avi');
i = i + 1;
end
else % else file name exists and enum disabled
fileName = []; % file will not be created
end
end
if(isempty(fileName))
sprintf('AVI file not created as file already exists.')
else
aviobj = avifile(fileName, 'fps', AVIfps, 'compression', 'Cinepak', 'quality', 100);
end
end
%% Setup figure and plot
% Create figure
fig = figure('NumberTitle', 'off', 'Name', '6DOF Animation');
if(FullScreen)
screenSize = get(0, 'ScreenSize');
set(fig, 'Position', [0 0 screenSize(3) screenSize(4)]);
elseif(~isempty(Position))
set(fig, 'Position', Position);
end
set(gca, 'drawmode', 'fast');
lighting phong;
set(gcf, 'Renderer', 'zbuffer');
hold on;
axis equal;
grid on;
view(View(1, 1), View(1, 2));
title(i);
xlabel(Xlabel);
ylabel(Ylabel);
zlabel(Zlabel);
% Create plot data arrays
if(strcmp(Trail, 'DotsOnly') || strcmp(Trail, 'All'))
x = zeros(numPlotSamples, 1);
y = zeros(numPlotSamples, 1);
z = zeros(numPlotSamples, 1);
end
if(strcmp(Trail, 'All'))
ox = zeros(numPlotSamples, 1);
oy = zeros(numPlotSamples, 1);
oz = zeros(numPlotSamples, 1);
ux = zeros(numPlotSamples, 1);
vx = zeros(numPlotSamples, 1);
wx = zeros(numPlotSamples, 1);
uy = zeros(numPlotSamples, 1);
vy = zeros(numPlotSamples, 1);
wy = zeros(numPlotSamples, 1);
uz = zeros(numPlotSamples, 1);
vz = zeros(numPlotSamples, 1);
wz = zeros(numPlotSamples, 1);
end
x(1) = p(1,1);
y(1) = p(1,2);
z(1) = p(1,3);
ox(1) = x(1);
oy(1) = y(1);
oz(1) = z(1);
ux(1) = R(1,1,1:1);
vx(1) = R(2,1,1:1);
wx(1) = R(3,1,1:1);
uy(1) = R(1,2,1:1);
vy(1) = R(2,2,1:1);
wy(1) = R(3,2,1:1);
uz(1) = R(1,3,1:1);
vz(1) = R(2,3,1:1);
wz(1) = R(3,3,1:1);
% Create graphics handles
orgHandle = plot3(x, y, z, 'k.');
if(ShowArrowHead)
ShowArrowHeadStr = 'on';
else
ShowArrowHeadStr = 'off';
end
quivXhandle = quiver3(ox, oy, oz, ux, vx, wx, 'r', 'ShowArrowHead', ShowArrowHeadStr, 'MaxHeadSize', 0.999999, 'AutoScale', 'off');
quivYhandle = quiver3(ox, oy, oz, uy, vy, wy, 'g', 'ShowArrowHead', ShowArrowHeadStr, 'MaxHeadSize', 0.999999, 'AutoScale', 'off');
quivZhandle = quiver3(ox, ox, oz, uz, vz, wz, 'b', 'ShowArrowHead', ShowArrowHeadStr, 'MaxHeadSize', 0.999999, 'AutoScale', 'off');
% Create legend
if(ShowLegend)
legend('Origin', 'X', 'Y', 'Z');
end
% Set initial limits
Xlim = [x(1)-AxisLength x(1)+AxisLength] * LimitRatio;
Ylim = [y(1)-AxisLength y(1)+AxisLength] * LimitRatio;
Zlim = [z(1)-AxisLength z(1)+AxisLength] * LimitRatio;
set(gca, 'Xlim', Xlim, 'Ylim', Ylim, 'Zlim', Zlim);
% Set initial view
view(View(1, :));
%% Plot one sample at a time
for i = 1:numPlotSamples
% Update graph title
if(strcmp(Title, ''))
titleText = sprintf('Sample %i of %i', 1+((i-1)*SamplePlotFreq), numSamples);
else
titleText = strcat(Title, ' (', sprintf('Sample %i of %i', 1+((i-1)*SamplePlotFreq), numSamples), ')');
end
title(titleText);
% Plot body x y z axes
if(strcmp(Trail, 'DotsOnly') || strcmp(Trail, 'All'))
x(1:i) = p(1:i,1);
y(1:i) = p(1:i,2);
z(1:i) = p(1:i,3);
else
x = p(i,1);
y = p(i,2);
z = p(i,3);
end
if(strcmp(Trail, 'All'))
ox(1:i) = p(1:i,1);
oy(1:i) = p(1:i,2);
oz(1:i) = p(1:i,3);
ux(1:i) = R(1,1,1:i);
vx(1:i) = R(2,1,1:i);
wx(1:i) = R(3,1,1:i);
uy(1:i) = R(1,2,1:i);
vy(1:i) = R(2,2,1:i);
wy(1:i) = R(3,2,1:i);
uz(1:i) = R(1,3,1:i);
vz(1:i) = R(2,3,1:i);
wz(1:i) = R(3,3,1:i);
else
ox = p(i,1);
oy = p(i,2);
oz = p(i,3);
ux = R(1,1,i);
vx = R(2,1,i);
wx = R(3,1,i);
uy = R(1,2,i);
vy = R(2,2,i);
wy = R(3,2,i);
uz = R(1,3,i);
vz = R(2,3,i);
wz = R(3,3,i);
end
set(orgHandle, 'xdata', x, 'ydata', y, 'zdata', z);
set(quivXhandle, 'xdata', ox, 'ydata', oy, 'zdata', oz,'udata', ux, 'vdata', vx, 'wdata', wx);
set(quivYhandle, 'xdata', ox, 'ydata', oy, 'zdata', oz,'udata', uy, 'vdata', vy, 'wdata', wy);
set(quivZhandle, 'xdata', ox, 'ydata', oy, 'zdata', oz,'udata', uz, 'vdata', vz, 'wdata', wz);
% Adjust axes for snug fit and draw
axisLimChanged = false;
if((p(i,1) - AxisLength) < Xlim(1)), Xlim(1) = p(i,1) - LimitRatio*AxisLength; axisLimChanged = true; end
if((p(i,2) - AxisLength) < Ylim(1)), Ylim(1) = p(i,2) - LimitRatio*AxisLength; axisLimChanged = true; end
if((p(i,3) - AxisLength) < Zlim(1)), Zlim(1) = p(i,3) - LimitRatio*AxisLength; axisLimChanged = true; end
if((p(i,1) + AxisLength) > Xlim(2)), Xlim(2) = p(i,1) + LimitRatio*AxisLength; axisLimChanged = true; end
if((p(i,2) + AxisLength) > Ylim(2)), Ylim(2) = p(i,2) + LimitRatio*AxisLength; axisLimChanged = true; end
if((p(i,3) + AxisLength) > Zlim(2)), Zlim(2) = p(i,3) + LimitRatio*AxisLength; axisLimChanged = true; end
if(axisLimChanged), set(gca, 'Xlim', Xlim, 'Ylim', Ylim, 'Zlim', Zlim); end
drawnow;
% Adjust view
if(numel(View) > 2)
view(View(i, :));
end
% Add frame to AVI object
if(~isempty(aviobj))
frame = getframe(fig);
aviobj = addframe(aviobj, frame);
end
end
hold off;
% Close AVI file
if(~isempty(aviobj))
aviobj = close(aviobj);
end
end

23
lib/rotation/eul2m.m Normal file
View File

@ -0,0 +1,23 @@
function [Cb2n_312, Cb2n_321] = eul2m(att)
% 将欧拉角转换为姿态阵
% 复用严龚敏老师的a2mat
%
% Input: att 单位rad
% 对于312((Z->X->Y))顺序对应att = [pitch(绕X轴) roll(绕Y轴) yaw(绕Z轴)]
% 对于3211(Z->Y->X)顺序对应att = [roll(绕X轴) pitch(绕Y轴) yaw(绕Z轴)]
% Outputs:
% Cb2n_312: 312欧拉角顺序下转换后的Cb2n
% Cb2n_321: 321欧拉角顺序下转换后的Cb2n
s = sin(att); c = cos(att);
si = s(1); sj = s(2); sk = s(3);
ci = c(1); cj = c(2); ck = c(3);
Cb2n_312 = [ cj*ck-si*sj*sk, -ci*sk, sj*ck+si*cj*sk;
cj*sk+si*sj*ck, ci*ck, sj*sk-si*cj*ck;
-ci*sj, si, ci*cj ];
if nargout==2 % dual Euler angle DCM
Cb2n_321 = [ cj*ck, si*sj*ck-ci*sk, ci*sj*ck+si*sk;
cj*sk, si*sj*sk+ci*ck, ci*sj*sk-si*ck;
-sj, si*cj, ci*cj ];
end

21
lib/rotation/eul2q.m Normal file
View File

@ -0,0 +1,21 @@
function [Qb2n_312, Qb2n_321] = eul2q(eul)
[Cb2n_312, Cb2n_321] = eul2m(eul);
Qb2n_312 = m2q(Cb2n_312);
Qb2n_321 = m2q(Cb2n_321);
% r = eul(1);
% p = eul(2);
% y = eul(3);
% cp = cos(p / 2);
% sp = sin(p / 2);
% cy = cos(y / 2);
% sy = sin(y / 2);
% cr = cos(r / 2);
% sr = sin(r / 2);
%
% q(1) = cr*cp*cy + sr*sp*sy;
% q(2) = -cr*sp*sy + cp*cy*sr;
% q(3) = cr*cy*sp + sr*cp*sy;
% q(4) = cr*cp*sy - sr*cy*sp;
% q = q';
% Ends

16
lib/rotation/m2eul.m Normal file
View File

@ -0,0 +1,16 @@
function [eul_312, eul_321] = m2eul(Cb2n)
% 将姿态阵转为欧拉角
% 复用严龚敏老师的m2att
%
% Input: 姿态阵Cb2n: b系->n系的坐标变换矩阵
% Outputs:
% eul_312: 312(Z->X->Y)旋转顺序下的欧拉角: att = [pitch(绕X轴) roll(绕Y轴) yaw(绕Z轴)]
% eul_321: 321(Z->Y->X)旋转顺序下的欧拉角: att = [roll(绕X轴) pitch(绕Y轴) yaw(绕Z轴)]
eul_312 = [ asin(Cb2n(3,2));
atan2(-Cb2n(3,1),Cb2n(3,3));
atan2(-Cb2n(1,2),Cb2n(2,2)) ];
if nargout==2 % dual Euler angles
eul_321 = [ atan2(Cb2n(3,2),Cb2n(3,3));
asin(-Cb2n(3,1));
atan2(Cb2n(2,1),Cb2n(1,1)) ];
end

24
lib/rotation/m2q.m Normal file
View File

@ -0,0 +1,24 @@
function Qb2n = m2q(Cb2n)
% 姿态阵转四元数
%
% Input: Cb2n
% Output: Qb2n
%
C11 = Cb2n(1,1); C12 = Cb2n(1,2); C13 = Cb2n(1,3);
C21 = Cb2n(2,1); C22 = Cb2n(2,2); C23 = Cb2n(2,3);
C31 = Cb2n(3,1); C32 = Cb2n(3,2); C33 = Cb2n(3,3);
if C11>=C22+C33
q1 = 0.5*sqrt(1+C11-C22-C33);
q0 = (C32-C23)/(4*q1); q2 = (C12+C21)/(4*q1); q3 = (C13+C31)/(4*q1);
elseif C22>=C11+C33
q2 = 0.5*sqrt(1-C11+C22-C33);
q0 = (C13-C31)/(4*q2); q1 = (C12+C21)/(4*q2); q3 = (C23+C32)/(4*q2);
elseif C33>=C11+C22
q3 = 0.5*sqrt(1-C11-C22+C33);
q0 = (C21-C12)/(4*q3); q1 = (C13+C31)/(4*q3); q2 = (C23+C32)/(4*q3);
else
q0 = 0.5*sqrt(1+C11+C22+C33);
q1 = (C32-C23)/(4*q0); q2 = (C13-C31)/(4*q0); q3 = (C21-C12)/(4*q0);
end
Qb2n = [q0; q1; q2; q3];

8
lib/rotation/mnormlz.m Normal file
View File

@ -0,0 +1,8 @@
function Cb2n = mnormlz(Cb2n)
% 姿态阵单位化,防止矩阵蠕变
for k=1:5
% Cnb = 0.5 * (Cnb + (Cnb')^-1); % Algorithm 1
Cb2n = 1.5*Cb2n - 0.5*(Cb2n*Cb2n')*Cb2n; % Algorithm 2
end
% Algorithm 3: [s,v,d] = svd(Cnb); Cnb = s*d'; % in = s*v*d' = s*d' * d*v*d'; out = s*d'

27
lib/rotation/q2eul.m Normal file
View File

@ -0,0 +1,27 @@
function [eul_312, eul_321] = q2eul(Qb2n)
% 四元数转欧拉角
%
% Input: 四元数Qb2n
% Outputs:
% eul_312: 312(Z->X->Y)旋转顺序下的欧拉角: att = [pitch(绕X轴) roll(绕Y轴) yaw(绕Z轴)]
% eul_321: 321(Z->Y->X)旋转顺序下的欧拉角: att = [roll(绕X轴) pitch(绕Y轴) yaw(绕Z轴)]
q0 = Qb2n(1);
q1 = Qb2n(2);
q2 = Qb2n(3);
q3 = Qb2n(4);
roll = -atan2( 2*( q1*q3 - q0*q2 ) , q0*q0 - q1*q1 - q2*q2 + q3*q3);
pitch = asin( 2*(q0*q1 + q2*q3) );
yaw = -atan2(2*( q1*q2 - q0*q3 ), q0*q0 - q1*q1 + q2*q2 - q3*q3);
eul_312 = [pitch; roll; yaw];
if nargout==2 % dual Euler angles
roll = atan2( 2*( q0*q1 + q2*q3 ) , 1 - 2*q1*q1 - 2*q2*q2);
pitch = asin( 2*(q0*q2 - q1*q3) );
yaw = atan2(2*( q0*q3 + q1*q2 ), 1 - 2*q2*q2 - 2*q3*q3);
eul_321 = [roll; pitch; yaw];
end

15
lib/rotation/q2m.m Normal file
View File

@ -0,0 +1,15 @@
function Cb2n = q2m(Qb2n)
% 四元数转姿态阵
%
% Input: Qb2n
% Output: Cb2n
%
q11 = Qb2n(1)*Qb2n(1); q12 = Qb2n(1)*Qb2n(2); q13 = Qb2n(1)*Qb2n(3); q14 = Qb2n(1)*Qb2n(4);
q22 = Qb2n(2)*Qb2n(2); q23 = Qb2n(2)*Qb2n(3); q24 = Qb2n(2)*Qb2n(4);
q33 = Qb2n(3)*Qb2n(3); q34 = Qb2n(3)*Qb2n(4);
q44 = Qb2n(4)*Qb2n(4);
Cb2n = [ q11+q22-q33-q44, 2*(q23-q14), 2*(q24+q13);
2*(q23+q14), q11-q22+q33-q44, 2*(q34-q12);
2*(q24-q13), 2*(q34+q12), q11-q22-q33+q44 ];

2
lib/rotation/qconj.m Normal file
View File

@ -0,0 +1,2 @@
function qout = qconj(qin)
qout = [qin(1); -qin(2:4)];

10
lib/rotation/qmul.m Normal file
View File

@ -0,0 +1,10 @@
function q = qmul(q1, q2)
% 四元数相乘
%
% Inputs: Q1 Q2, 四元数和矩阵一样,不满足交换律
% Outputs: Q
%
q = [ q1(1) * q2(1) - q1(2) * q2(2) - q1(3) * q2(3) - q1(4) * q2(4);
q1(1) * q2(2) + q1(2) * q2(1) + q1(3) * q2(4) - q1(4) * q2(3);
q1(1) * q2(3) + q1(3) * q2(1) + q1(4) * q2(2) - q1(2) * q2(4);
q1(1) * q2(4) + q1(4) * q2(1) + q1(2) * q2(3) - q1(3) * q2(2) ];

24
lib/rotation/qmulv.m Normal file
View File

@ -0,0 +1,24 @@
function vo = qmulv(q, vi)
% 向量通过四元数做3D旋转
%
% Inputs: q - Qb2n
% vi - 需要旋转的向量
% Output: vout - output vector, such that vout = q*vin*conjugation(q)
%
% See also q2mat, qconj, qmul.
% qi = [0; vi];
% qo = ch_qmul(ch_qmul(q,qi),ch_qconj(q));
% vo = qo(2:4,1);
qo1 = - q(2) * vi(1) - q(3) * vi(2) - q(4) * vi(3);
qo2 = q(1) * vi(1) + q(3) * vi(3) - q(4) * vi(2);
qo3 = q(1) * vi(2) + q(4) * vi(1) - q(2) * vi(3);
qo4 = q(1) * vi(3) + q(2) * vi(2) - q(3) * vi(1);
vo = vi;
vo(1) = -qo1 * q(2) + qo2 * q(1) - qo3 * q(4) + qo4 * q(3);
vo(2) = -qo1 * q(3) + qo3 * q(1) - qo4 * q(2) + qo2 * q(4);
vo(3) = -qo1 * q(4) + qo4 * q(1) - qo2 * q(3) + qo3 * q(2);

9
lib/rotation/qnormlz.m Normal file
View File

@ -0,0 +1,9 @@
function q = qnormlz(q)
% 四元数归一化
q = q/norm(q);
if(q(1)<0)
q(1) = -q(1);
q(2) = -q(2);
q(3) = -q(3);
q(4) = -q(4);
end

5
lib/rotation/rotx.m Normal file
View File

@ -0,0 +1,5 @@
function Cb2n = rotx(theta)
% 3D初等旋转 theta为旋转角度rad
Cb2n = [1 0 0; 0 cos(theta) -sin(theta); 0 sin(theta) cos(theta)];
end

5
lib/rotation/roty.m Normal file
View File

@ -0,0 +1,5 @@
function Cb2n = roty(theta)
% 3D初等旋转 theta为旋转角度rad
Cb2n = [cos(theta), 0 sin(theta); 0 1 0; -sin(theta) 0 cos(theta)];
end

5
lib/rotation/rotz.m Normal file
View File

@ -0,0 +1,5 @@
function Cb2n = rotz(theta)
% 3D初等旋转 theta为旋转角度rad
Cb2n = [cos(theta) -sin(theta) 0; sin(theta) cos(theta) 0; 0 0 1];
end

19
lib/rotation/rv2m.m Normal file
View File

@ -0,0 +1,19 @@
function m = rv2m(rv)
% 等效旋转矢量转换为旋转矩阵
%
% Input: rv - 旋转矢量
% Output: m -旋转矩阵 Cb2n 严龚敏 2.2.23
% m = I + sin(|rv|)/|rv|*(rvx) + [1-cos(|rv|)]/|rv|^2*(rvx)^2
% where rvx is the askew matrix or rv.
nm2 = rv'*rv; % 旋转矢量的模方
if nm2<1.e-8 % 如果模方很小,则可用泰勒展开前几项求三角函数
a = 1-nm2*(1/6-nm2/120); b = 0.5-nm2*(1/24-nm2/720); % a->1, b->0.5
else
nm = sqrt(nm2);
a = sin(nm)/nm; b = (1-cos(nm))/nm2;
end
VX = askew(rv);
m = eye(3) + a*VX + b*VX^2;

12
lib/rotation/rv2q.m Normal file
View File

@ -0,0 +1,12 @@
function q = rv2q(rv) % 等效旋转矢量转换为变换四元数
nm2 = rv'*rv; % 旋转矢量的模方
if nm2<1.0e-8 % 如果模方很小,则可用泰勒展开前几项求三角函数
q0 = 1-nm2*(1/8-nm2/384);
s = 1/2-nm2*(1/48-nm2/3840);
else
nm = sqrt(nm2);
q0 = cos(nm/2);
s = sin(nm/2)/nm;
end
q = [q0; s*rv];

18
lib/rotation/uv2q.m Normal file
View File

@ -0,0 +1,18 @@
%%
%Cartographer SLAM 姿态算法分析
%刘兴华
%电邮:xiphix@126.com
%微信:xiphix
function [q] = uv2q(v1, v2)
%Finding quaternion representing the rotation from one vector to another
nv1 = v1/norm(v1);
nv2 = v2/norm(v2);
if norm(nv1+nv2)==0
q = [1 0 0 0]';
else
half = (nv1 + nv2)/norm(nv1 + nv2);
q = [nv1'*half; cross(nv1, half)];
end
end

17
lib/sv2atti.m Normal file
View File

@ -0,0 +1,17 @@
function [Cb2n] = sv2atti(acc)
acc = acc / norm(acc);
Cb2n(3,:) = acc ;
C3 = Cb2n(3,:);
if Cb2n(3,1) > 0.5
C2 = [Cb2n(3,2), -Cb2n(3,1), 0];
else
C2 = [0, Cb2n(3,3), -Cb2n(3,2)];
end
C2 = C2 / norm(C2);
C1 = cross(C2, C3);
Cb2n = [C1; C2; C3];
end

21
lib/triangulate.m Normal file
View File

@ -0,0 +1,21 @@
% 迭代式多边测距
function p = triangulate(anchor_pos, p, pr)
% 基站个数
n = size(anchor_pos, 2);
% 获得当前位置与各个基站的距离
r = vecnorm(anchor_pos - p);
% 求得H矩阵
H = (anchor_pos - p) ./ r;
H =-H';
% 迭代用户距离
p = p + (H'*H)^(-1)*H'*(pr - r)';
end