oh-my-posh:一个好看的终端提示符怎么装
oh-my-posh 是个跨平台的终端提示符美化工具,支持 PowerShell、bash、zsh、fish、cmd。它用 JSON 配置文件定义提示符长什么样,可以显示当前目录、git 分支、执行时间、电池、天气、各种编程语言版本等。
安装Windows 上最方便的是用 winget:
winget install JanDeDobbeleer.OhMyPosh
装完会多出一个 oh-my-posh.exe。macOS 和 Linux 可以用 Homebrew:
brew install jandedobbeleer/oh-my-posh/oh-my-posh
或者直接用官方安装脚本:
curl -s https://ohmyposh.dev/install.sh | bash
字体oh-my-posh 的很多主题用到 Nerd Font 图标。如果看到方块或乱码,说明当前字体没有这些字形。
推荐装 MesloLGM Nerd Font 或 Cascadia Code PL。Windows Terminal 里在设置 → 外观 → 字体里选上。VS Code 终端同样要在 se ...
DOCX Pipeline v1.2.0:Markdown 转 Word,顺便把 LaTeX 公式也塞进去
DOCX Pipeline 是个把 Markdown 转成中文 DOCX 的命令行工具。双后端:Pure Python 零外部依赖,Pandoc 质量更高。v1.2.0 补上了纯 Python 后端对 LaTeX 数学公式的支持,写论文、出报告时不用硬装 Pandoc。
下面先说怎么用,再简单提实现。
安装Python 3.10 以上:
pip install git+https://github.com/redamancy231-create/docx-pipeline.git
核心依赖会一起装好:python-docx、PyYAML、click、Pillow。Pandoc 和 mermaid-cli 是可选的,后面按需安装。
一分钟上手1. 初始化项目docx-pipeline init --project-dir ./my-doc --template report --name "技术报告"
执行完会在 ./my-doc 下生成 project.yaml,里面包含页面、字体、路径、后端开关等所有配置。
四个内置模板:
模板
场景
默认后端
d ...
横向平滑:抹掉 GPR 剖面上的窄条纹
增益把深部放大后,一些横向窄的条纹、绕射尾、振铃尾也跟着被放大,在 B-scan 上冒成一片斜纹或竖条毛刺。横向平滑就是沿测线方向做均值替换,把这些窄东西糊掉,横向连续的层位则保留。
和背景去除的区别背景去除是「减均值」:删掉横向共有的成分,留下横向局部的点目标。
横向平滑是「替换为均值」:把每道替换成左右几道的平均,抹掉横向局部的窄条纹,留下横向共有的连续层位。
两者保留的东西正好相反:
操作
去掉
保留
背景去除
减均值
横向共有(直达波、水平横条)
横向局部(钢筋双曲线)
横向平滑
替换为均值
横向局部(绕射尾、条纹)
横向共有(连续层位)
单级盒式的问题最简单的做法是用一个矩形窗做一级滑动平均。但它的频率响应是 sinc 函数,旁瓣大——阻带里的纹波压不干净,条纹会留残影。
两级盒式更干净做两级串联:第一级用较大窗做主要平滑,第二级用小窗磨掉第一级留下的边缘台阶。两个矩形核卷积后等效成一个梯形核,频响近似 sinc²,旁瓣比单级小得多。
实测对比(window=10):
归一化频率
单级响应
两级响应
两级压到
0.05(通带)
0. ...
给图像做完增益别再用整张图的百分位定色阶
处理一批灰度图,深部信号弱、看不清,就加了道增益(gain)把深部的幅值往上提。调完增益打开图一看,奇怪了——本来是想让深部亮起来,结果观感是浅部反而变灰了,深部也没见亮多少。第一反应是增益写错了,查了半天代码没问题。
后来才反应过来,这不是增益的 bug,是显示的色阶在捣乱。
全局百分位定色阶,会被放大后的极值撑大显示这种动态范围很大的灰度数据,一般不会直接用最小最大值定 vmin/vmax——少数极大值(比如直达波那种)会把范围撑得太大,绝大多数数据挤在一头,图就「非黑即白」。常见的做法是用百分位,比如取第 1 和第 99 百分位作为显示的上下限,把长尾削掉,层次就出来了。
问题在于,这个百分位是整张图算的。增益一上去,深部那些本来不强、但被放大了的反射,幅值被抬得很高,整张图的 p99 跟着水涨船高(实测能从不到一万飙到二十多万)。色阶的上限一拉高,那些没被增益放大的浅部数据,在新的色阶里就全被挤向中灰了。
所以调大增益参数,看到的不是「深部变亮」,而是「浅部变灰」——因为色阶被深部的极值撑宽了,浅部在更宽的尺度里自然显得更暗。本质是灰度级被重新分配了,不是数据本身变暗 ...
GPR 时间增益与 AGC:把深部提亮的两条路
手电筒照得越远越暗,雷达波也一样。深部反射回波比浅部弱很多,不补一下,剖面深处就是一片黑。
深部为什么衰减电磁波往下传,能量衰减来自两方面:
球面扩散:能量摊开,随距离平方变弱(∝ 1/r²);
介电吸收:介质吸能,随距离指数衰减(∝ e^(−αz))。
深部反射体回波可能比浅部弱 40~60dB。
时间增益:按时间乘一条固定曲线造一条随时间单调增长的曲线 $G(t)$,每道信号逐样点乘上去。浅处乘得小,深处乘得大。
三种常见形状:
$$G(t)=\begin{cases}1+p,t & \text{linear}\ e^{,p,t} & \text{exp}\ t^{,p}\text{ 归一化} & \text{power}\end{cases}$$
指数型最贴合指数吸收的物理。设 p=0.051,dt=0.054ns,512 个样点:
位置
时间
增益
相当于
浅
0 ns
1.00
+0 dB
中
10 ns
1.66
+4.4 dB
深
27.6 ns
4.09
+12.2 dB
最深处 ...
16 位整数写回文件有符号和无符号要分开 clamp
把一批 16 位采样的数据读进来做了点预处理(去直流、去背景这种减法操作),再写回原来的二进制文件给别的专业软件打开。读进来处理的时候一切正常,写回去打开一看,有的文件好好的,有的直接满屏噪点、糊成一团。同样的代码、同样的处理,结果天差地别,卡了好一阵。
根子是 16 位整数到底按有符号还是无符号解读,不是统一的,取决于文件格式。
同样 16 位,有的围绕 0 有的围绕 32768这批数据两种格式:一种是无符号 16 位(unsigned),数据的载波零点在 32768,数值在 065535 之间抖;另一种是有符号 16 位(signed),围绕 0 抖,范围是 −3276832767。同样是两个字节 00 80,按无符号读是 32768,按有符号读(小端)是 −32768,完全是两码事。
读的时候按各自格式来,没问题。问题出在写回。
减法预处理之后有了负值去直流、去背景这类操作本质是减法:估一个背景再从原数据里减掉。减完之后,数据就从「围绕 32768」或者「围绕 0」变成了「围绕 0」,而且必然出现负值(原来低于背景的地方)。
写回有符号格式(围绕 0)的那种文件,很直接——数据本 ...
逆向一种私有二进制格式从文件头和 magic 字节说起
手上有一批设备采集下来的二进制数据,没文档,只有几组不同后缀的文件,要把它们读出来画图。全程靠 hex 编辑器一点点猜,记一下逆向这种私有二进制格式的大概路数,顺便记一个踩得很疼的 off-by-one。
先把头和样点数摸清楚这种采集文件,基本结构都是「一个文件头 + 后面一大片采样数据」。第一步是估文件头多大。把文件丢进 hex 编辑器,前面一段往往是固定模式、跟后面的「数据区」长得不一样,头一般是个整数(512、1024、2048 这种)。这批文件摸下来头是 1024 字节。
样点数通常藏在文件头的某个偏移里。怎么找——算一道简单除法:(文件总大小 - 头) / 每道字节数 = 道数,而 每道字节数 = 样点数 × 每个样点的字节。如果数据是 16 位(2 字节)存的,那去文件头里找一个值,使得「头之后的字节数 / 2 / 这个值」是个整数、而且这个值本身是合理的样点数(比如 512)。这批文件样点数在头偏移 4 的位置,值是 512。
到这里,最朴素的读法就有了:跳过 1024 头,后面按 int16 一道一道读:
nsamp = 512 ...
雷达数据做增益到底在补什么
雷达数据做完前面的清洗,最后一步往往是加增益(gain)。深部反射看不见、一团黑,加完增益深部就亮起来了。但增益到底在补什么、补多少有讲究,不是无脑把深部拉亮就行。另外有篇单独讲做完增益之后显示色阶的坑,这篇只讲增益算法本身。
为什么要补:深部比浅部弱太多电磁波往地下走,越深衰减越厉害,所以深处的反射回到天线时已经弱了一大截。一道信号画出来,浅部反射很强、深部几乎贴着噪声底,深部想看的目标(比如衬砌后面的脱空)就淹没在里面。增益的作用就是把这个随深度掉下去的幅度,人为往上提一提,让深浅在图上都能看见。
衰减有两种,数学形式不一样深部变弱这件事,根上有两个来源,得分开看。一个是几何扩散——波往外是球面扩散的,能量摊到越来越大的球面上,幅度按幂律(大约 1/距离)往下掉。另一个是介电吸收——介质本身把电磁能吃掉一部分,这个是按指数 e^(−α·深度) 掉的。
关键在于这俩数学形式不同:一个是幂律、一个是指数。所以想补偿,也得用对应的形式去补,硬拿一个套另一个补不齐。
两种增益对应两种衰减增益常见的两种类型正好对应上:power 型按幂律补,对应几何扩散;exp 型按指数补,对应介 ...
一张登录许可证启动时到底该查哪些
给桌面软件接了一套登录许可证,启动的时候要过一串校验,任一条不过就直接锁应用。licence 防破解的一些具体坑(时间回拨、指纹漂移)之前写过,这里不重复,主要想理一下:一张许可证启动时到底该查哪些项,以及其中几个不那么直观的、容易漏掉的检查。
整体是个串行链条,前面是许可证本身合不合法,后面是运行环境对不对,最后才是用户名密码。哪一环断了都锁,不是「前面过了后面就随便」。
许可证本体这几项先是把许可证解析出来,格式不对、签名解析失败,直接锁。然后是有效期,过期了锁——但有效期的判断会被改系统时间绕掉,这块得配合本地单调时间记录来防回拨,之前那篇说过的思路,这里不展开。接着是硬件指纹比对,确认现在这台机器就是许可证绑的那台,防一份 licence 拷来拷去到处用。
这几项是基础,大多数做 licence 的人都会想到。
版本号得跟软件版本对上这条我觉得容易被忽略。许可证里带一个版本号字段,校验的时候不能只看它「有没有」,得看它是不是等于当前软件的版本。
为什么重要——因为软件会迭代。一份在 v2 时代签发的 licence,里面版本号写的是 2,用户拿着它去跑 v3,如果不校验版本,v ...
注册机的管理员密码别明文写在配置文件里
做了个注册机给桌面软件签发许可证,注册机本身有个管理员解锁密码——不是最终用户的密码,是防止谁都能进来乱签 license 的那道门。一开始图省事,密码明文写在 gradle.properties 里,程序运行的时候读出来比对。能用是能用,但越想越不对劲。
问题在于,这个密码随包一起发出去了。gradle.properties 里的值,打包完之后会以某种形式落到产物里,最差就是直接生成一个 register.properties 跟着 exe 走。破解者拿到发版包,翻一翻配置文件,明文密码就在那儿躺着,这道门等于没上锁。
后来改成这样:密码在打包阶段就转成 MD5,然后通过 exe4j 的 VM 参数固化进 exe,运行时直接从系统属性里读 MD5 来比对,全程不落外部配置文件。
打包脚本里大致是:
// 打包时把明文密码算成 MD5,塞进 exe4j 的 VM 参数def pwdMd5 = md5(project.property('registerPassword'))def vmArgs = "-Dregister.password.md5=$ ...
