佳能镜头EOS系统EF协议逆向工程(三)解码算法
目录
数据结构
解码算法
解码效果
这篇文章基于上两篇文章继续,
佳能镜头EOS系统EF协议逆向工程(一)转接环电路设计_佳能ef自动对焦协议_岬淢箫声的博客-CSDN博客本文属于专栏——工业相机。此专栏首先提供我人工翻译的法语文档部分,然后通过STM32F103C8T6控制佳能镜头,最后协同上位机或者NVIDIA Xavier实现自动对焦。还有一个用处不大的River文档,它知道如何让相机和镜头通信,也许对当前的摄影实践几乎没有帮助。尽管如此,一些应用程序可能需要独立订购物镜的主要功能。撇开工业世界及其特殊机器不谈,在另一个品牌的图像采集系统上安装佳能光学元件是不可能的,而且是有罪的,无论是出于经济原因还是纯粹的技术原因,获得的组合提供了其他不可用的功能。https://blog.csdn.net/caoshiying/article/details/127609884?spm=1001.2014.3001.5502佳能镜头EOS系统EF协议逆向工程(二)逻辑分析仪测试_岬淢箫声的博客-CSDN博客本章描述了用于解密EF协议函数的思想和分析,如果其读数是可选的,则所获得结果的摘要将在专用章节中进行汇编,这仍然是正确理解EF协议函数的必要来源。随着函数的测试和逐步解码,它们的描述和列表中使用的注释将在过程中变得越来越精确,因为以前的分析或推理错误不会被强制纠正。所使用的逻辑分析仪是一个小型号的低成本8输入TTL USBEE AX Pro,所使用的软件是制造商的标准套件,可免费下载。软件不允许编辑或删除部分结果,某些读数或时钟意外激活,因为通电会导致SPI字解码不同步。
https://blog.csdn.net/caoshiying/article/details/129057004?spm=1001.2014.3001.5502
数据结构
逻辑分析仪没有特别要求,某宝上的大部分逻辑分析仪可以用。数据格式要求很简单,举便如下:
; CSV, generated by libsigrok4DSL 0.2.0 on Fri Jul 29 10:17:48 2022
; Channels (3/16)
; Sample rate: 10 MHz
; Sample count: 50.896 M Samples
Time(s), CLK, DLC, DCL
0,1,1,1
0.0498275,1,1,0
0.0498285,1,1,1
0.0498697,1,1,0
0.049876,1,1,1
0.0499793,1,1,0
0.0499986,0,1,0
0.050005,1,1,0
分号开头表示注释,第一列是时间,这个时间是相对开始捕获的时间,用单词elapse表示列标题更合适。这款逻辑分析仪软件导出数据的表头就是这么写的,无所谓了。第二列是CLK信号,CLK是时钟的简写,搞硬件的同学是不是很熟悉呢?第三列是DLC信号。DLC是Data Lens to Camera的首字母缩写。第四列是DCL,DCL是Data Camera to Lens首字母缩写。所有信号用1表示高电平,0表示低电平。佳能相机的电平为3.3V。
我使用Qt写的解码工具,CMake工程代码如下:
cmake_minimum_required(VERSION 3.20)
project(TAMRON VERSION 1.0)find_package(Qt5 COMPONENTS Widgets REQUIRED PATHS $ENV{Qt515_DIR})
link_libraries(Qt5::Widgets)
add_link_options(/SUBSYSTEM:CONSOLE)
file(GLOB TAMRON_SRCS *.cpp *.h *.ui *.qrc *.rc)
add_executable(${PROJECT_NAME} ${TAMRON_SRCS})
target_compile_definitions(${PROJECT_NAME} PRIVATE $<IF:$<CONFIG:DEBUG>,CWDEBUG,CWNDEBUG>)
Qt515_DIR环境变量是必须的。
解码算法
算法是关键是解决ACK干扰。核心思路是寻找U型特征的连续信号。源代码只有一个main.cpp,代码如下:
#include <QApplication>
#include <QMainWindow>
#include <QFileDialog>
#include <QFile>
#include <QMessageBox>
#include <QTextStream>
#include <QDebug>
#include <QFileInfo>
#include <QMetaEnum>typedef struct _spi_signal_t
{uint32_t line_no;//行号double elapse;//消耗的时间,6位小数,单位为秒,存储化为usbool clk;//时钟是否高位bool dcl;//主机信号是否高位bool dlc;//从机信号是否高位
} spi_signal_t;typedef struct _spi_data_t
{uint32_t order;//序号uint32_t start_line;//信号起始行号uint32_t end_line;//信号结束行号uint8_t dcl;//主机命令uint8_t dlc;//从机返回int frame_no;//字节所属帧号bool bad;//是否发生丢失bit的情况double elapse;//消耗的时间double frequency;//实际通讯频率,存储为KHz
} spi_data_t;//信号数据中的U型结构,全部为引用指针,不管理内存
typedef struct _u_shape_t
{spi_signal_t *high_left;//U槽左端spi_signal_t *low_left;//U槽底左边spi_signal_t *low_right;//U槽底右边spi_signal_t *high_right;//U槽右端spi_signal_t *forward;//遍历前进到达的位置bool ok;//此U型结构是否能用int index_cursor;//信号列表遍历时当前的位置
} u_shape_t;typedef QSharedPointer<spi_signal_t> pspi_signal_t;
typedef QSharedPointer<spi_data_t> pspi_data_t;
typedef QSharedPointer<u_shape_t> pu_shape_t;
#define ZERR_CAPTION u8"系统错误"
#define ZOK_CAPTION u8"系统提示"//CSV文件文件转换为信号数据
QList<pspi_signal_t> resolve_csv(const QString &zcsv_path);
//信号数据转换为真实的主机与从机之间交换的信息
QList<pspi_data_t> resolve_signal(const QList<pspi_signal_t> &ps);
//保存信息
void save_spi_data(const QString &zcsv_path, const QList<pspi_data_t> &ds);
//寻找信号中的U形,返回下一个U形右端索引+1,返回值index_cursor与istart_cursor相等表示结束,ok为false表示出错
pu_shape_t find_u_shape(const QList<pspi_signal_t> &ps, int istart_cursor, int ilen);
//保存程序日志
void save_log(QtMsgType type, const QMessageLogContext &cxt, const QString &zlog);
//解析帧
void resolve_frame(QList<pspi_data_t> &ds);//入口函数
int main(int argc, char **argv)
{qInstallMessageHandler(save_log);QApplication app(argc, argv);auto zcsv_path = QFileDialog::getOpenFileName(nullptr,u8"选择一个CSV文件","D:/tamron");if (zcsv_path.isEmpty())return 1;auto ps = resolve_csv(zcsv_path);if (ps.isEmpty())return 2;auto ds = resolve_signal(ps);if (ds.isEmpty())return 3;resolve_frame(ds);save_spi_data(zcsv_path, ds);return 0;
}//CSV文件文件转换为信号数据
QList<pspi_signal_t> resolve_csv(const QString &zcsv_path)
{QFile f(zcsv_path);QList<pspi_signal_t> ps;if (!f.open(QFile::ReadOnly | QFile::Text)){QMessageBox::warning(nullptr, ZERR_CAPTION, f.errorString());return ps;}QTextStream ts(&f);ts.skipWhiteSpace();//DSView导出数据有5行注释和1行标头,应当跳过。for (short i = 0; i < 4; i++){auto zline = ts.readLine();if (ts.atEnd() || !zline.startsWith(";")){QMessageBox::warning(nullptr, ZERR_CAPTION, u8"无效的CSV文件。");break;}}ts.readLine();if (ts.atEnd())return ps;uint32_t iline = 6;while (!ts.atEnd()){QString zline = ts.readLine();if (zline.isEmpty())continue;QStringList cols = zline.split(u8",");if (cols.length() < 4){QMessageBox::critical(nullptr, ZERR_CAPTION, QString(u8"第%1行无效数据").arg(iline));return ps;}spi_signal_t r;r.line_no = iline++;if (iline >= UINT32_MAX){QMessageBox::warning(nullptr,ZERR_CAPTION,u8"input data amount exceed system capacity.");break;}r.elapse = cols[0].toDouble() * 1000000;r.clk = cols[1].toInt() != 0;r.dlc = cols[2].toInt() != 0;r.dcl = cols[3].toInt() != 0;pspi_signal_t p = QSharedPointer<spi_signal_t>::create(r);ps.append(p);}qInfo() << u8"合计" << ps.length() << u8"个信号\n";return ps;
}//寻找信号中的U形,返回下一个U形右端索引+1,返回值index_cursor与istart_cursor相等表示结束,ok为false表示出错
pu_shape_t find_u_shape(const QList<pspi_signal_t> &ps, int istart_cursor, int ilen)
{pu_shape_t pu(new u_shape_t{0,});u_shape_t *u = pu.data();u->index_cursor = istart_cursor;//1.寻找U槽//1.1.寻找下降沿,定位U槽左端while (u->index_cursor < ilen && ps[u->index_cursor]->clk)u->index_cursor++;u->forward = ps[u->index_cursor].data();if (u->index_cursor - 1 < 0){u->ok = false;u->index_cursor++;return pu;}if (u->index_cursor + 1 >= ilen){qWarning() << "data end on finding u->high_left and u->low_left";u->ok = false;return pu;}u->high_left = ps[u->index_cursor - 1].data();u->low_left = ps[u->index_cursor].data();u->forward = u->low_left;
#ifdef DEBUGqInfo() << "u begin: " << u->high_left->line_no;
#endifu->index_cursor++;if (u->index_cursor >= ilen){qWarning() << "data end on finding u->low_right";u->ok = false;return pu;}//1.2.寻找上升沿,定位U槽底有多长while (u->index_cursor < ilen && !ps[u->index_cursor]->clk)u->index_cursor++;u->low_right = ps[u->index_cursor - 1].data();u->forward = u->low_right;if (u->index_cursor >= ilen){qWarning() << "data end on finding u->high_right";u->ok = false;return pu;}//1.3.寻找下降沿,定位U槽右端//while (u->index_cursor < ilen && ps[u->index_cursor]->clk)// u->index_cursor++;u->high_right = ps[u->index_cursor].data();u->forward = u->high_right;//1.4.如果U槽的时间跨度大于28us则不是数据传输,暂不处理佳能中的拱门double fuspan1 = u->high_right->elapse - u->low_right->elapse;double fuspan2 = u->low_right->elapse - u->low_left->elapse;if (fuspan1 > 28 || fuspan1 < 0 || fuspan2 > 28 || fuspan2 < 0){qWarning() << "elapse time out: " << fuspan2 << ", " << fuspan1;u->ok = false;}elseu->ok = true;
#ifdef DEBUGif (u->ok)qInfo() << "u end: " << u->forward->line_no;
#endifreturn pu;
}//信号数据转换为真实的主机与从机之间交换的信息
QList<pspi_data_t> resolve_signal(const QList<pspi_signal_t> &ps)
{int icursor = 0;//遍历ps列表的索引int ilen = ps.length(); //总长度减2,双指针遍历int iorder = 1;QList<pspi_data_t> ds;if (ilen < 17){QMessageBox::critical(nullptr, ZERR_CAPTION, u8"数据量太少。");return ds;}while (icursor < ilen - 17){int ifor_cursor = icursor;//发现问题则回到U槽右端int iu_cursor = 0;pspi_data_t byte(new spi_data_t);pu_shape_t pu;byte->order = iorder;byte->frame_no = 0;byte->dcl = 0;byte->dlc = 0;byte->bad = false;byte->start_line = 0;byte->end_line = 0;byte->elapse = 0;byte->frequency = 0;qInfo() << "byte " << iorder << " begin: " << ps[icursor]->line_no;for (short i = 0; i < 8; i++){pu = find_u_shape(ps, ifor_cursor, ilen);ifor_cursor = pu->index_cursor;if (iu_cursor == 0)iu_cursor = ifor_cursor;if (pu->ok){
#ifdef DEBUGqInfo() << "byte " << iorder << " bit " << i << " at " << pu->low_left->line_no;
#endifif (byte->start_line == 0){byte->start_line = pu->low_left->line_no;byte->elapse = pu->low_left->elapse;}if (pu->high_right->dcl)byte->dcl |= 1 << (7 - i);if (pu->high_right->dlc)byte->dlc |= 1 << (7 - i);}else{byte->bad = true;break;}}if (byte->bad){icursor = iu_cursor;qWarning() << "byte " << iorder << " break: " << pu->forward->line_no;}else{icursor = ifor_cursor;byte->end_line = pu->forward->line_no;byte->elapse = pu->forward->elapse - byte->elapse;byte->frequency = 8000 / byte->elapse;qInfo() << "byte " << iorder << " end: " << pu->high_right->line_no;icursor++;//跳过第9个下降沿while (ps[icursor]->clk && icursor < ilen)icursor++;while (!ps[icursor]->clk && icursor < ilen)icursor++;iorder++;//80KHz下每个bit用时约为13us,1个byte不超过120usif (byte->elapse > 120)byte->bad = true;ds.append(byte);}}return ds;
}//保存信息
void save_spi_data(const QString &zcsv_path, const QList<pspi_data_t> &ds)
{//避免多次执行QChar的构造函数和析构函数static const QChar cfill('0');QFileInfo fi(zcsv_path);QString zresolve_path = fi.dir().absoluteFilePath(fi.baseName() + "resolve.csv");QFile f(zresolve_path);if (!f.open(QFile::ReadWrite | QFile::Text | QFile::Truncate)){QMessageBox::warning(nullptr, ZERR_CAPTION, f.errorString());return;}QTextStream ts(&f);ts << u8" command,sequence, start, end, dcl, dlc,elapse(us),frequency(KHz),bad\n";for (const pspi_data_t &d : ds){// char c = ' ';// if (d->dlc >= 0x20 && d->dlc <= 0x7E && d->dcl == 0)// c = (char)d->dlc;// if (c == ',')// c = ' ';QString zline = QString("%1,%2,%8,%9, %3, %4,%5,%6,%7\n").arg(d->frame_no, 8).arg(d->order, 8).arg(d->dcl, 2, 16, cfill).arg(d->dlc, 2, 16, cfill).arg(d->elapse, 10, 'f', 0).arg(d->frequency, 14, 'f', 0).arg(d->bad, 3).arg(d->start_line, 8).arg(d->end_line, 8);// .arg(c, 4);zline = zline.toUpper();ts << zline;}ts.flush();f.close();QMessageBox::information(nullptr, ZOK_CAPTION, u8"数据转换完成。");qInfo() << u8"数据转换完成。";
}void save_log(QtMsgType type, const QMessageLogContext &cxt, const QString &zlog)
{static QFile f;static QTextStream output(stdout);static QTextStream ts;if (!f.isOpen()){QDir d(QApplication::applicationDirPath());f.setFileName(d.absoluteFilePath("main.log"));if (!f.open(QFile::Append | QFile::Text | QFile::ReadWrite)){QMessageBox::warning(nullptr, ZERR_CAPTION, u8"无法打开日志文件。");return;}ts.setDevice(&f);ts.setAutoDetectUnicode(true);}QString ztype;switch (type){case QtDebugMsg:ztype = u8"DEBG";break;case QtWarningMsg:ztype = u8"WARN";break;case QtCriticalMsg:ztype = u8"CRIT";break;case QtFatalMsg:ztype = u8"FATA";break;case QtInfoMsg:ztype = u8"INFO";break;default:Q_ASSERT(false);break;}ts << ztype << ", " << zlog.toUtf8() << "\n";ts.flush();output << ztype << " " << zlog.toUtf8() << "\n";output.flush();
}//解析帧
void resolve_frame(QList<pspi_data_t> &ds)
{if (ds.length() < 2)return;int ilen = ds.length();int iframe = 0;for (int i = 0; i < ilen; i++){if (ds[i]->dcl > 0)iframe++;ds[i]->frame_no = iframe;}
}
解码效果
如下图所示

解码结果中通信频率、0A与AA应答信号、0x06与0x05的转动信号与实际匹配,说明解码成功。如果想要更多的通信规律,请与我私聊。下一篇讲解常见的指令。
相关文章:
佳能镜头EOS系统EF协议逆向工程(三)解码算法
目录 数据结构 解码算法 解码效果 这篇文章基于上两篇文章继续, 佳能镜头EOS系统EF协议逆向工程(一)转接环电路设计_佳能ef自动对焦协议_岬淢箫声的博客-CSDN博客本文属于专栏——工业相机。此专栏首先提供我人工翻译的法语文档部分&…...
搞互联网吧,线下生意真不是人干的
搞互联网吧,线下生意真不是人干的 应该是正月初几里吧,好巧不巧的被迫去参加了一下我们初中同学的聚会。其实毕业这么多年,无论大学,高中还是中学,类似的聚会我都是能躲则躲,有特别想见的同学也都是私下单…...
MySQL性能调优与设计——MySQL中的索引
MySQL中的索引 InnoDB存储引擎支持以下几种常见索引:B树索引、全文索引、哈希索引,其中比较关键的是B树索引。 B树索引 InnoDB中的索引自然也是按照B树来组织的,B树的叶子节点用来存放数据。 聚集索引/聚簇索引 InnoDB中使用了聚集索引&…...
这5个代码技巧,让我的 Python 加速了很多倍
Python作为一种功能强大的编程语言,因其简单易学而受到很多初学者的青睐。它的应用领域又非常广泛:科学计算、游戏开发、爬虫、人工智能、自动化办公、Web应用开发等等。 而在数据科学领域中,Python 是使用最广泛的编程语言,并且…...
Sphinx+Scws 搭建千万级准实时搜索应用场景详解
目标: 一、搭建准确的千万级数据库的准实时搜索(见详情) 二、实现词语高亮(客户端JS渲染,服务器端渲染,详见7.3) 三、实现搜索联想(输入框onchange,ajax请求搜索,取10条在…...
kafka缩容后,使用tcpdump抓包找到还在连接的用户
1、使用tcpdump抓包监控端口9092 tcpdump src port 9092 16:23:27.680835 IP host01.XmlIpcRegSvc > 192.168.168.1.36199: Flags [R.], seq 0, ack 1493547965, win 0, length 0 16:23:27.681877 IP host01.XmlIpcRegSvc > 192.168.168.2.50416: Flags [R.], seq 0, ac…...
Spring
Spring Spring 是什么? Spring 是于 2003 年兴起的一个轻量级的,IOC 和 AOP 的 Java 开发框架,它 是为了简化企业级应用开发而生的。 Spring有几大特点如下 轻量级的 Spring 框架使用的 jar 都比较小,一般在 1M 以下或者几百 kb。Spring 核 心功能…...
vue2版本《后台管理模式》(中)
文章目录前言一、创建一个文件夹 utils 里面新增一个 setToken.js 文件(设置token验证)二 、创建一个api文件夹 新增 service.js (axios拦截器)三、在api文件夹里 新增一个 api.js 来接收数据(把api封装哪里需要某项数据直接引入就…...
网络游戏开发-服务器篇
1.网络 网络分为弱联网和强联网。 1.弱联网 弱联网是客户端连接到服务端发送一个请求,然后由服务端回应一个内容,这是单向传输的方式,服务端是无法主动给客户端发送消息的,服务端相应请求之后会自动关闭连接。 缺点:传输采用明文,通过抓包可以看到明文信息,安全性不太…...
智慧校园源码:电子班牌,支持手机移动端以及web端对班牌设备的管控
▶ 智慧校园系统有源码,有演示! (电子班牌)设备管理: 1、 管理员查看全校电子班牌设备信息:含有(班级信息、软件版本、设备型号、开关机信息、班牌截屏信息、教室编号、设备ID、设备描述、在线状态、离线状…...
研报精选230216
目录 【行业230216东吴证券】环保行业月报:2023M1环卫新能源渗透率大增至11.91%,上海地区渗透率高达77%【行业230216国元证券】国元新食饮:一图君:22年白酒产量:同降6.2%【行业230216浙商证券】农林牧渔点评报告&#…...
在华为MateBook Ego的arm windows 11上安装hyper-V虚拟机
入手一台华为matebook Ego的笔记本,由于想要测试一些arm的驱动功能,经常会把系统搞蓝屏,于是想安装一个虚拟机,于是试了vmware ,visual-box,由于本机是arm架构上面两个软件都无法进行正常安装,可能是由于有…...
OpenCV Canny边缘检测
本文是OpenCV图像视觉入门之路的第13篇文章,本文详细的介绍了Canny边缘检测算子的各种操作,例如:Canny算子进行边缘检测等操作。 Canny函数是OpenCV中用于执行边缘检测的函数之一,其参数包括: threshold1:…...
C#.Net正则表达式学习笔记
C#.Net正则表达式学习笔记 在处理字符串时,你会经常有查找符合特定条件的字符串的需求,比如判断一串电话号码是否符合格式、一个邮箱是否符合格式、一个密码是否包含了字母大小写等等。 正则表达式(Regular expressions)用于匹配文本,使用一…...
矩阵理论复习(十二)
已知方阵A的不变因子: 求谱半径求矩阵级数判断矩阵幂级数的收敛性 若矩阵B的某个算子范数小于1,则I-B可逆。 矩阵分析 任何相容矩阵范数都存在与之相容的向量范数。 盖尔圆盘定理一的证明 椭圆范数的证明 若||.||是Cm上的向量范数,A为…...
大数据框架之Hadoop:HDFS(七)HDFS 2.X新特性
7.1集群间数据拷贝 scp实现两个远程主机之间的文件复制 scp -r hello.txt roothadoop103:/root/hello.txt // 推 push scp -r roothadoop103:/root/hello.txt hello.txt // 拉 pull scp -r roothadoop103:/root/hello.txt roothadoop104:/root //是通过本地主机中…...
Fluent工作目录
1 工作目录定义工作目录(working directory)是一种文件存储路径设置方式。基于工作目录的方法,写文件时只需要指定文件名,而不需要指定完全的文件路径,从而简化程序编写,对不同操作系统环境有更好的适应性。…...
Learning C++ No.10【STL No.2】
引言: 北京时间:2023/2/14/23:18,放假两个月,没有锻炼,今天去跑了几圈,一个字,累,感觉人都要原地升天了,所以各位小伙伴,准确的说是各位卷王,一定…...
【java 高并发编程之JUC】2w字带你JUC从入门到精通
点击查看脑图目录地址,实时更新 1 什么是 JUC 1.1 JUC 简介 在 Java 中,线程部分是一个重点,本篇文章说的 JUC 也是关于线程的。JUC 就是 java.util .concurrent 工具包的简称。这是一个处理线程的工具包,JDK 1.5 开始出现的。 1.2 进程与…...
QCon演讲实录(下):多云管理关键能力实现与解析-AppManager
在上篇中,我们已经基本了解了多云管理。现在,我们将深入探讨多云管理关键能力实现:AppManager。 什么是AppManager? 上面我们讲了理论、我们自己使用的交付流程和整体架构,下面我们进入关键能力实现与解析的环节&…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
