0%

原本用于编译openwrt的Deepin在一次升级中挂掉了,于是尝试在Manjaro下编译。

sudo pacman -S base-devel asciidoc binutils bzip2 gawk gettext git ncurses zlib patch unzip lib32-glibc subversion flex gcc-multilib p7zip msmtp lib32-openssl texinfo xmlto qemu upx libelf autoconf automake libtool gettext

比ubuntu下的依赖项少了一些,不过用默认配置跑了一遍正常生成镜像。

7月11日更新

由于众所周知的原因golang在编译时会提示 dial tcp 172.217.160.113:443: i/o timeout 并导致编译失败。可以通过使用代理解决:

export GO111MODULE=on
export GOPROXY=https://goproxy.cn
make download -j8
make -j2 V=s

如此之后顺利编译:

ls lede/build_dir/target-mipsel_24kc_musl/linux-ramips_mt76x8/tmp/

openwrt-ramips-mt76x8-glinet_vixmini-initramfs-kernel.bin
openwrt-ramips-mt76x8-glinet_vixmini-squashfs-sysupgrade.bin
openwrt-ramips-mt76x8-glinet_vixmini-squashfs-sysupgrade.bin.sig
openwrt-ramips-mt76x8-glinet_vixmini-squashfs-sysupgrade.bin.ucert

sudo pacman -S texlive-most texlive-langchinese

manjaro软件仓库里的 texlive 比较精简,没有附带宏包手册所以 texdoc 不能用, latex 初学者还是建议去CTAN下载完整镜像手动安装。我是为了编译之前写好的文件所以没有手册影响不大。(原本写作环境Deepin挂掉了)

前言

单位 G3930 电脑的windows 10系统最近更新几次失败后就频繁死机,被迫重装,于是grub就无了。为啥要重装windows,而不是Linux only呢,因为我搞不定Linux下的打印机 Orz Windows 10的打印机其实也有bug,最著名的便是 KB5006670 导致远程smb打印机连接失败,一顿折腾后参考真解决Win10最新补丁导致共享打印问题 通过替换 win32spl 文件解决了。

下载Windows 10

微软官网直接提供了ISO下载地址,不需要安装工具: https://www.microsoft.com/en-us/software-download/windows10ISO

制作安装U盘

sudo pacman -Syu woeusb
sudo woeusb --device /home/manjaro/迅雷下载/Win10_21H2_Chinese_x64.iso /dev/sdb

windows下我习惯用rufus制作各类安装U盘,Linux下 woeusb 是个不错的命令行工具,用法和 dd 类似,更多用法可以通过 man 查询。注意提供的文件名不要太复杂,比如

sudo woeusb --device /home/manjaro/迅雷下载/Win10_21H2_Chinese(Simplified)_x64.iso /dev/sdb

就会执行失败,推测原因是圆括号不能正常识别。

安装Windows 10

傻瓜操作

修复 grub

以下步骤在 Manjaro live usb 中进行,或者其他自己惯用的Linux发行版,制作好安装U盘后启动即可。

su
mount /dev/sda8 /mnt
mount mount --bind /dev /mnt/dev
mount --bind /proc /mnt/proc
mount --bind /sys /mnt/sys
chroot /mount
grub-install --target i386-pc /dev/sda
update-grub

有两处需要注意。其一是Linux位置,可以通过 fdisk 查看,本例中为 sda8 。其二是 grub 安装。我这里 EFI 安装失败,所以指定了 i386-pc 这种老式方式。重启后问题圆满解决。

背景介绍

为了分析自己的驾驶行为,最近斥巨资35元在PDD上买了2个OBD盒子。结合Car Scanner得到了CSV格式的数据。在此基础上,计划利用R进行分析。

参考资料

  1. R语言教程 ,北京大学出品,适合快速上手。

  2. R语言中变量命名规则与反引号的使用 ,阐述了R中变量名的自动转换问题。

将数据从CSV文件导入R

data <- read.csv("2022-05-17+08-26-13.csv", header=TRUE, as.is=TRUE, check.names=FALSE)

程序中的选项header=TRUE指明第一行作为变量名行, 选项as.is=TRUE说明字符型列要原样读入而不是转换为因子(factor),check.names=FALSE禁止变量名检查与自动转换。如果不禁用变量名检查,导入R的列名如下:

> colnames(data)
 [1] "time"                                     
 [2] "发动机转速..rpm."                         
 [3] "已耗燃油..L."                             
 [4] "已耗燃油.总计...L."                       
 [5] "平均速度..km.h."                          
 [6] "平均速度..GPS...km.h."                    
 [7] "气压..kPa."                               
 [8] "氧传感器1宽范围.电流.mA...mA."            
 [9] "氧传感器1宽范围.等价比..."                
[10] "燃料价格...."                             
[11] "燃料价格.总计....."                       
[12] "燃油平均消耗.升.100公里...L.100km."       
[13] "燃油平均消耗.升.100公里..10.sec..L.100km."
[14] "燃油平均消耗.升.100公里..总计...L.100km." 
[15] "燃油瞬时消耗.升.100公里...L.100km."       
[16] "省油器.基于燃油系统状态和油门位置...."    
[17] "瞬时发动机功率.根据燃油消耗...kW."        
[18] "瞬时燃油消耗.升.小时...L.h."              
[19] "节气门位置...."                           
[20] "计算增压..bar."                           
[21] "路程..km."                                
[22] "路程.总计...km."                          
[23] "车辆加速..g."                             
[24] "车速..km.h."                              
[25] "进气歧管绝对压力..kPa."                   
[26] "进气温度...."                             
[27] "速度..GPS...km.h."                        
[28] "高度..GPS...m."                           
[29] "Latitude"                                 
[30] "Longtitude"                               
[31] "X"                                        

禁用检查后,结果如下:

> colnames(data)
 [1] "time"                                        
 [2] "发动机转速 (rpm)"                            
 [3] "已耗燃油 (L)"                                
 [4] "已耗燃油(总计) (L)"                        
 [5] "平均速度 (km/h)"                             
 [6] "平均速度 (GPS) (km/h)"                       
 [7] "气压 (kPa)"                                  
 [8] "氧传感器1宽范围 电流(mA) (mA)"             
 [9] "氧传感器1宽范围 等价比 ()"                   
[10] "燃料价格 (¥)"                                
[11] "燃料价格(总计) (¥)"                        
[12] "燃油平均消耗(升/100公里) (L/100km)"        
[13] "燃油平均消耗(升/100公里) 10 sec (L/100km)" 
[14] "燃油平均消耗(升/100公里)(总计) (L/100km)"
[15] "燃油瞬时消耗(升/100公里) (L/100km)"        
[16] "省油器(基于燃油系统状态和油门位置) ()"     
[17] "瞬时发动机功率(根据燃油消耗) (kW)"         
[18] "瞬时燃油消耗(升/小时) (L/h)"               
[19] "节气门位置 (%)"                              
[20] "计算增压 (bar)"                              
[21] "路程 (km)"                                   
[22] "路程(总计) (km)"                           
[23] "车辆加速 (g)"                                
[24] "车速 (km/h)"                                 
[25] "进气歧管绝对压力 (kPa)"                      
[26] "进气温度 (℃)"                               
[27] "速度 (GPS) (km/h)"                           
[28] "高度 (GPS) (m)"                              
[29] "Latitude"                                    
[30] "Longtitude"                                  
[31] ""  

禁用变量检查与转换后,可以不加考虑地通过列名调用指定列:

> data$"发动机转速 (rpm)"
   [1]   NA   NA   NA  911  910   NA   NA  907  908  902  909  899  895  895
  [15]  896  895  895  902  910  910  898  910  902  898  902  896  897  897
  [29]  895  903  907  902  903  903  901  905  900  900  900  898  904  905
  [43]  905  902  899  899  901  900  905  898  897  900  900  898  905  902
  [57]  899  897  897  896  906  897  903  904  899  904  908  908  901  898
  [71]  899  893  893  903  902  909  904  897  891  891  903  902  903  892
  [85]  889  895  892  892  882  886  889  896  892  887  887  890  896  893
  [99]  893  893  884  895  890  893  891  880  880  883  890  889  886  889
 [113]  892  892  886  890  900  890  886  895  895  886  887  895  889  891

(数据为节选)

绘制折线图

字符串转时间

分析某一变量随时间变化趋势,首先尝试绘制折线图。这里有一个小小的问题:数据中的时间格式如下:

> data$time
   [1] "08:26:19.571" "08:26:19.573" "08:26:19.575" "08:26:20.597"
   [5] "08:26:20.771" "08:26:20.933" "08:26:21.100" "08:26:21.266"
   [9] "08:26:21.424" "08:26:21.612" "08:26:21.769" "08:26:21.938"

这种格式的数据R不能直接使用,需要进行转换,即将字符串转换为R可以理解的时间。

显而易见,原始数据中的时间格式为hms格式,R提供了一个同名的第三方包:

install.packages("hms")

如果没用用户权限,包就会安装在home下某一个目录内。

然后加载这个包:

library(hms)

进行类型转换:

x <- as_hms(data$time)

缺失值处理

OBD通过采样读取数据流,受采样率限制,不可能在同一时间读取到全部参数,所以值缺失现象在样本数据中随处可见。为了绘制折线图,需要对缺失值进行适当处理。

发动机转速随时间变化折线图

library(hms)
rpm <- data.frame(x = data$time, y = data$"发动机转速 (rpm)")
rpm <- rpm[!is.na(rpm$y),]
plot(as_hms(rpm$x), rpm$y, type = "b")

然后就得到了一个比较简陋的发动机转速随时间变化折线图。由于数据采样比较频繁,不显示标点的”l”类型更合适。添加主标题,xy轴标题,得到的折线图好看了一些:

plot(as_hms(rpm$x), rpm$y, type = "l", main = "远景x3 pro cvt 发动机转速随时间变化折线图", xlab = "时间", ylab = "发动机转速(rpm)")

最大值、最小值、平均值、分位数

> max(data$"发动机转速 (rpm)", na.rm=TRUE)
[1] 1837
> min(data$"发动机转速 (rpm)", na.rm=TRUE)
[1] 724
> mean(data$"发动机转速 (rpm)", na.rm=TRUE)
[1] 1199.271
> quantile(data$"燃油平均消耗(升/100公里) (L/100km)", na.rm=TRUE)
         0%         25%         50%         75%        100% 
   10.43989    11.06329    13.97581    31.61916 57746.89340 

summary提供了更快捷的数据摘要:

> summary(data$"发动机转速 (rpm)")
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
    724     895    1251    1199    1460    1837    1002 
> 

异常值处理

将异常值标记为NA

在整体数据的summary中,发现 高度 (GPS) (m) 的最小值为零:

高度 (GPS) (m)
Min.   :  0.0
1st Qu.:144.4
Median :147.3
Mean   :134.1
3rd Qu.:149.8
Max.   :176.3
NA's   :2690 

结合实际,显然这些零值是错误的,应当视为缺失值一并处理:

> data$"高度 (GPS) (m)"[data$"高度 (GPS) (m)"==0] <- NA
> summary(data$"高度 (GPS) (m)")
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  140.3   145.2   147.8   148.6   150.0   176.3    2780

剔除零值后数据变得更有意义了。

拓展阅读:如何理解R中因子(factor)的概念?,猴子的回答同时阐明了 因子 的概念。

library(hms)
rpm <- data.frame(x = data$time, y = data$”高度 (GPS) (m)”)
rpm <- rpm[!is.na(rpm$y),]
plot(as_hms(rpm$x), rpm$y, type = “l”, main = “高度随时间变化折线图”, xlab = “时间”, ylab = “高度”)

library(hms)
rpm <- data.frame(x = data$time, y = data$”燃油平均消耗(升/100公里) (L/100km)”)
rpm <- rpm[!is.na(rpm$y),]
rpm <- rpm[!is.na(quantile(rpm$y,probs=c(.3, .75),na.rm=TRUE)),]
plot(as_hms(rpm$x), rpm$y, type = “l”, main = “燃油平均消耗(升/100公里) (L/100km)”, xlab = “时间”, ylab = “燃油平均消耗(升/100公里) (L/100km)”)

y=log2(x)
x=2^y

Rust官方教程中Packages,Crates与Modules一章略有些晦涩,特别是Packages与Crates的关系。这部分翻来覆去读了几遍,结合别人项目和Rust Packages vs Crates大概弄懂了。

Package

Package 为其中的最高单位:

$ cargo new my-project
     Created binary (application) `my-project` package

执行cargo new命令,便得到了一个package,大概对应其他语言中的 project ?

Package 具备如下特点:
d

  1. Package 至少包装了一个 crate;
  2. Package 是可发布的;
  3. Package 可包含一个或零个 library crate;
  4. Package 可包含多个 binary crate;
  5. 当将Package添加到自己的依赖时(在Cargo.toml中添加),实际上是使用了package中的 library crate。

Crate

  1. crate 是一种组织代码的形式;
  2. crate不是binary就是library;
  3. crate不能独立发布,只能作为package的成员发布;
  4. 对于编译器,crate更多起到命名空间作用

Rust官方教程读到智能指针一章就有些迷糊了,毕竟上次接触指针还是N年前的 谭浩强 C语言 XD 而且只看不练基础没法牢固,所以打算对前面章节进行一些实验,研究下可行的编程范例。

这个例子是对结构体的实验。之前用python时 字典 + 列表 足以应对绝大部分场景。Rust中 Vec 有些类似列表,不过 Vec 中成员类型要求一致。 Vec 提供了push和pop方法用于增删成员,也可以通过索引或者get方法调用成员。注意get方法返回 Option<&T> 类型需要自己match一下。

此外有几点需要留意:

  1. 迭代器。使用迭代器会发生所有权的转移!所以该借用(borrow)就借用吧。
  2. 字符串。需要留意 + 运算符对于字符串的实现,大致看起来这样:
    fn add(self, s: &str) -> String {
    
    所以对于一般 let s3 = s1 + &s2 的情形, s1 所有权会发生转移。

如果要避免所有权转移的麻烦,使用format!即可。

#[derive(Debug)]
struct Rect1 {
    id: u64,
    name: String,
}
impl Rect1 {
    fn new(id:u64, name: String) -> Rect1 {
        Rect1 {
            id,
            name,
        }
    }
}
fn main() {
    let mut id: u64;
    let mut name: String;
    let mut clbox: Vec<Rect1> = Vec::new();

    for i in 0..5 {
        id = i;
        name = "a".to_string() + &id.to_string();
        //name = format!("{}{}", "a", &id.to_string());
        //let tmp_rect = Rect1::new(id, name);
        clbox.push(Rect1::new(id, name));
    }

    for item in &clbox {
        println!("{} - {}", item.id, item.name);
    }
    println!("\n");
    for item in &clbox {
        println!("{:?}", item);
    }
}

sudo pacman-key --init && sudo pacman-key --populate && sudo pacman -Syyu

网上随便搜来的,解决了问题。

Manjaro ARM 21.12 已发布了一段时间,照例测试了VIM2镜像。除了软件包例行更新外,引导方式发生了重大改变。在21.10及之前版本中,系统启动顺序为 BSP U-BOOT - Mainline U-BOOT - Linux Kernel ,21.12抛弃了 Mainline U-BOOT ,由 BSP U-BOOT 直接加载 Linux Kernel

理论上此次改动缩短了启动流程,项目维护成本更小。不过实际测试来看,取消 Mainline U-BOOT 后,系统暴露出一些问题:

  1. HDMI 无输出。并非在全部场景下均无输出,系统刚上电时接通显示器,HDMI输出正常。如果在系统完全启动后再连接显示器,HDMI无任何输出。
  2. 关机重启异常。测试了Mainline、Odroid、khadas内核,执行关机或重启命令后系统均为进入僵死状态,只能通过断电恢复。

问题1对于无头服务器影响轻微,即便是第一次开机初始化配置也可以通过SSH远程完成,完成初始化配置前root用户无密码可远程登录。

问题2只要不频繁重启主机影响也不大。除了更新内核外,必须重启的场景不多。关机基本不存在的,低功耗零噪音7X24开机运行无压力。

值得一提的是,放弃主线内核后,有线网卡MAC地址不会每次重启后变化了,所以不再需要用nmtui提供的mac address clone功能固定地址。

当然对于需要从低版本更新到21.12的盒子,还是建议在 /etc/pacman.conf 中,将 boot-vim2 添加到 IgnorePkg 中。直接更新 boot-vim2 会干掉 Image,而且不会更新启动配置文件,稳妥起见不要乱折腾为宜。

回顾

过去在R3300-M上进行过几次不成功的试验。无论是balbes150的Armbian 20.10还是目前使用的Manjaro Linux ARM,使用所提供的安装脚本均无法启动。最近发现论坛上名为pista一网友遇到类似问题并成功解决,看看我们能不能成功复现。

https://forum.armbian.com/topic/18902-s905-failed-to-boot-from-emmc/

设备

Minix Neo U1 … S905, 2GB RAM / 16GB Flash ,搭配meson-gxbb-vega-s95-meta.dtb运行Armbian_20.10_Arm-64_bullseye_current_5.9.0.img。既然和R3300-M同为GXBB S905,那么如果Minix Neo U1可以从emmc启动linux,R3300-M也没有问题。

默认安装脚本从EMMC启动Armbian_20.10遇到的问题

** No partition table - mmc 2 **

与R3300-M当时遇到的问题一致。

妥协方案

emmc分区保留前700MB,剩余空间分给ROOTFS,然后利用TF卡启动。在R3300-M上也曾试验成功,但我们的目的是完全脱离TF卡,于是pista开始了深入研究。

纯emmc方案

pista成功从EMMC分区启动了Armbian 20.10。关键所在是使用 mmc read 而不是 load/fatload 来加载 *kernelramdisk ,以及 dtb 。检查了Manjaro Linux ARM, /boot/boot.ini 内容如下:

ODROIDN2-UBOOT-CONFIG
#ODROIDC2-UBOOT-CONFIG

if test "${devtype}" = ""; then setenv devtype "mmc"; fi

if fatload ${devtype} ${devnum} 0x1000000 u-boot.ext; then go 0x1000000; fi;

看来需要想办法用 mmc read 替换 load/fatload

深入研究

emmc分区理论

首先对emmc分区进行描述,pista的看起来这样:

blkdevparts=mmcblk1:209715200@1463812096(boot),-@1673528320(root)

209715200Byte对应200MByte,1463812096Byte对应1396MByte,1673528320约合1596MByte,并非精确匹配。

查询可知blkdevparts命令用法: https://www.kernel.org/doc/html/latest/block/cmdline-partition.html

通过学习,可知pista的分区描述为boot分区大小200MB(offset为1396MB,即emmc的前1396MB跳过,保留给为Amlogic的老uboot),这样EMMC的前1596MB就分配完毕了,209715200加上1463812096等于1673527296,比1673528320刚好小了1024。至此分区全貌一目了然:

1 - 1463812096 (前1396MB) 预留
1463812097 - 1673527296 boot分区,大小200MB
1673527297 - 1673528320 预留,大小1MB
1673528321 -           root分区,占据剩余空间

pista为了保险起见,在EMMC分区头部保留了足足1396MB大小的空间。我们的R3300-M的EMMC容量总共才3728MB,可以适当减小预留空间,比如700MB。这样root分区大概有2.5GB空间可用,可以装下Manjaro ARM Minimal或者openwrt等无GUI界面的Linux发行版了。

emmc实际分区操作

pista没有给出具体命令,只是介绍了大概过程:

  1. 预留出 /dev/mmcblk1 头部1396MB空间;
  2. 新建 /dev/mmcblk1p1/dev/mmcblk1p2两个分区,分别对应boot和root分区。root分区内容直接复制过去即可,重点是boot分区,需要使用dd命令,将 mainline ubootkernelramdiskdtb 写入预定义的区块位置,例如:
    dd if=zImage of=/dev/mmcblk1p2 bs=512 seek=2859008 count=54841'
    
  3. 对于Amlogic的uboot,使用 mmc read 替代 fatload 来加载全部文件,例如:
    mmc read 0x08080000 0x2BA3FC 0xD639
    
  4. 在被链式启动的Mainline uboot中,使用 booti 0x08080000 0x13000000 0x08008000 替代 bootcmd 来启动系统。

    重现流程

    pista并没有给出可无脑照抄的流程,所以我们试着重现具体过程。

    建立分区

    目标

    保留EMMC分区前700MB,创建200MB的 BOOT_MNJRO 分区,然后剩余空间创建 ROOT_MNJRO 分区。分区名称来自在Manjaro ARM Minimal中用 e2label 查看的结果。fdisk结果如下:
    Device         Boot  Start       End   Sectors   Size Id Type
    /dev/mmcblk0p1       62500    500000    437501 213.6M  c W95 FAT32 (LBA)
    /dev/mmcblk0p2      500001 250347519 249847519 119.1G 83 Linux
    
    利用 df -h 查看,BOOT_MNJRO 使用了54MB大小空间,故分配200MB应该够用。(Manjaro ARM分配了214MB)

    操作

    然后就不会了 XD 求指点

提要

先回顾一下,我们之前为R3300-M刷入了ATV固件,目的是提升CPU主频;在TF卡上刷入了VIM2的Manjaro ARM Minimal 21.10镜像,并使用meson-gxbb-p201.dtb启动。随后,用linux-odroid替换了主线内核。配合编译WIFI驱动、更换软件源、设置NetworkManager等操作,现在Manjaro ARM已经能够稳定运行,为运行各种服务做好了充分准备。不过默认命令行界面略显简陋,下文将介绍如何用较少的硬件资源使Manjaro ARM Minimal看起来更现代、更好用。

配置nano高亮

nano是Manjaro ARM Minimal的默认文本编辑器,稍作修改使其更好用:

安装拓展高亮

sudo pacman -Sy nano-syntax-highlighting

应用高亮

编辑 /etc/nanorc ,加入以下两行:

include "/usr/share/nano/*.nanorc"
include "/usr/share/nano-syntax-highlighting/*.nanorc"

重新登录后生效。

美化命令行界面

silver安装

sudo pacman -Sy silver ttf-nerd-fonts-symbols

silver配置

创建 ~/.config/silver/silver.toml 文件,内容如下:

[[left]]
name = "status"
color.background = "black"
color.foreground = "white"

[[left]]
name = "user"
color.background = "yellow"
color.foreground = "black"

[[left]]
name = "dir"
color.background = "blue"
color.foreground = "black"

[[left]]
name = "git"
color.background = "green"
color.foreground = "black"

[[left]]
name = "cmdtime"
color.background = "magenta"
color.foreground = "black"

然后修改 ~/.bashrc 文件。默认文件大概这样:

#
# ~/.bashrc
#

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

alias ls='ls --color=auto'
PS1='[\u@\h \W]\$ '

修改成这样:

#
# ~/.bashrc
#

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

PS1='[\u@\h \W]\$ '

# configure silver command prompt
export SILVER_ICONS=nerd
source <(silver init)

# source bash aliases
source ~/.bash_aliases

现在还没有 ~/.bash_aliases ,手动创建一个,一会儿要用。

老命令替代

安装

sudo pacman -Sy exa bat chafa

配置

编辑 ~/.bash_aliases ,加入以下内容:

alias ls="exa -aglh --icons"
alias tree="exa --tree --icons"
alias cat="bat"
alias chafa="chafa -f sixel"

exa和bat分别是ls和cat的现代化替代。chafa比较有意思,可以将图片转换为在命令行终端中显示的字符画。

保存后,重新登录或者执行source ~/.bashrc生效。

安装文件浏览器

sudo pacman -Ss mc ranger

mc是老前辈,ranger是后生,根据个人喜好使用。