blockimgdiff中方法分析01

Android OTA 升级实战指南:从原理到代码实现 本文详细介绍了Android OTA升级的完整流程,从基础概念到代码实现,涵盖Recovery升级和A/B无缝升级两种方式。通过实战代码示例和常见问题解决方案,帮助开发者掌握OTA升级包制作、升级流程实现及调试技巧,提升系统更新效率和用户体验。 阅读详情

关于blockimgdiff文件,本篇文档将详细分析初步生成transfer对象和生成diff的字典,并对几个重要方法 进行解析.   

一、传入blockimgdiff的参数分析  

二、blockimgdiff中方法分析

 

一、传入blockimgdiff的参数分析

我们都知道生成差分包从ota_from_target_file.py 的WriteBlockIncrementalOTAPackage方法开始,有部分 流程我在之前的文档做包问题中提到过,有一部分我直接copy过来,核心代码部分

以上截图首先拿到system_src,system_tgt,通过getimage方法
  system_src = GetImage("system", OPTIONS.source_tmp)
  system_tgt = GetImage("system", OPTIONS.target_tmp)
而getimage的返回值为spare_img对象
return sparse_img.SparseImage(path, mappath, clobbered_blocks)

传入system_diff = common.BlockDifference("system", system_tgt, system_src,
                                       check_first_block,
                                       version=blockimgdiff_version,
                                       disable_imgdiff=disable_imgdiff)

common.blockdifference,跳转到common.py  class BlockDifference(object):中
主要做了两步
1.生成blockimgdiff对象,传入对应src,tgt参数,也就是之前获得的spare_img对象
b = blockimgdiff.BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,
                                    version=self.version,
                                    disable_imgdiff=self.disable_imgdiff)
2.调用blockimgdiff的Compute方法
b.Compute(self.path)

以上就是调用到blockimgdiff的大致流程,接下来进入我们的主要分析部分

  二、blockimgdiff中方法分析

1.blockimgdiff init  其中有一些参数大概做下说明
  def __init__(self, tgt, src=None, threads=None, version=4,
               disable_imgdiff=False):
    #声明两个空的字典,后面会用到
    self.src_basenames = {}
    self.src_numpatterns = {}
    #声明了一个空的集合.重点关注,后面会使用到
    self.transfers = []
    # The range sets in each filemap should comprise a partition of
    # the care map.
    #src tgt 也就是传进来的sparse_img对象,那么这其中给到我们的有用的信息,caremap和filemap
    #0-32766 32768-32769 32865-32866 .......360925-364543 这些数字就是caremap,也就是
    system分区中文件占用的block块,为什么会有空的呢.因为sparse格式里面会存在头文件等
    print(src.care_map)
    #/system/bin/applypatch    打印出来可以看到file_map中的key值为system.map文件中的文件名
    print(src.file_map.keys())
    #<RangeSet("130771-130825")>  values为该文件所占的block区间
    print(src.file_map.values())
    self.AssertPartition(src.care_map, src.file_map.values())
    self.AssertPartition(tgt.care_map, tgt.file_map.values())
    
    这里简单说明下,本身python 中只有range列表,上面出现的rangeset是Android为了装载所占用block的
    区间导入的Rangelib例如上面的130771-130825

 

2.Compute(self, prefix): 接下来就是调用compute生成最终的差分包中transfer.list的形态

,本次我们先 介绍前三个方法     

#名称转换,将基础版本中的文件名转换为    

self.AbbreviateSourceNames()     

#生成Transer对象    

self.FindTransfers()      #

Find the ordering dependencies among transfers (this is O(n^2)     # in the number of transfers).    

#生成gose_before,gose_after两个字典,相当于存放着基础版本文件和升级版本的差异和差异block的大小     self.GenerateDigraph()     

第一个方法只是做了简单的替换,我们将着重分析后两个方法,基本上每个方法将生成对应的数据为下一步的     计算做准备

3.self.AbbreviateSourceNames()
    for k in self.src.file_map.keys():
      #self.src.file_map.keys() 我们之前说明了self.src.file_map.keys这个方法获得的就是system.map
中的文件名,当然也包含文件路径,os.path.basename方法的作用就是抹去文件路径,只保留文件名称
例如:k=/system/bin/applypatch,那么b=applypatch
      b = os.path.basename(k)
      #print("AbbreviateSourceNames" + "++++b" + b)
      #以下代码的含义是python字典的写法,表示的是字典中key为b,values为k
      self.src_basenames[b] = k
      #sub为替换字符串的方法 [0-9]为正则表达式,表示如果b中有0-9的数字,会执行替换为#,重新赋值为b	
      b = re.sub("[0-9]+", "#", b)
      #print("AbbreviateSourceNames" + "----b" + b)
      #装载到src_numpatterns,key为b,values为k
      self.src_numpatterns[b] = k

4.def FindTransfers(self):方法分析

  print("Finding transfers...")

    empty = RangeSet()
    for tgt_fn, tgt_ranges in self.tgt.file_map.items():
      
      if tgt_fn == "__ZERO":
        print("tgt_fn == __ZERO"+ "++++tgt_fn=" + tgt_fn)
        # the special "__ZERO" domain is all the blocks not contained
        # in any file and that are filled with zeros.  We have a
        # special transfer style for zero blocks.
        src_ranges = self.src.file_map.get("__ZERO", empty)
        AddTransfer(tgt_fn, "__ZERO", tgt_ranges, src_ranges,
                    "zero", self.transfers)
        continue

      elif tgt_fn == "__COPY":
        print("tgt_fn == __COPY" + "++++tgt_fn=" + tgt_fn)
        # "__COPY" domain includes all the blocks not contained in any
        # file and that need to be copied unconditionally to the target.
        AddTransfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers)
        continue

      elif tgt_fn in self.src.file_map:
        print("tgt_fn in self.src.file_map" + "++++tgt_fn=" + tgt_fn)
        # Look for an exact pathname match in the source.
        AddTransfer(tgt_fn, tgt_fn, tgt_ranges, self.src.file_map[tgt_fn],
                    "diff", self.transfers, True)
        continue

      b = os.path.basename(tgt_fn)
      if b in self.src_basenames:
        print("b in self.src_basenames" + "++++tgt_fn=" + tgt_fn + "self.transfers=" + self.transfers)
        # Look for an exact basename match in the source.
        src_fn = self.src_basenames[b]
        AddTransfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],
                    "diff", self.transfers, True)
        continue

      b = re.sub("[0-9]+", "#", b)
      if b in self.src_numpatterns:
        print("b in self.src_numpatterns" + "++++tgt_fn=" + tgt_fn)
        # Look for a 'number pattern' match (a basename match after
        # all runs of digits are replaced by "#").  (This is useful
        # for .so files that contain version numbers in the filename
        # that get bumped.)
        src_fn = self.src_numpatterns[b]
        AddTransfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],
                    "diff", self.transfers, True)
        continue

      AddTransfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers)
在做包的log中可以看到,执行的打印finding transfer..
声明一个空的列表
    empty = RangeSet()
开启for循环,
    for tgt_fn, tgt_ranges in self.tgt.file_map.items():
遍历tgt_fn和tgt_ranges,这里面有很多的判断条件,大部分都
进入了
elif tgt_fn in self.src.file_map:
AddTransfer(tgt_fn, tgt_fn, tgt_ranges, 
self.src.file_map[tgt_fn],"diff", self.transfers, True)
也就是当tgt文件名存在与src中时,这个时候会进入addTranster
方法,并将一些参数传入,"diff" 是指的存在差分,self.transfers=[]
如果前五个条件都走完了,如果有APK都不符合,那么就进入最后的
AddTransfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers)
传入的src_fn,src_ranges为空,也就是作为新增文件了

这里其他类似的方法我们先不做分析,先只分析主线diff流程
AddTransfer/AddSplitTransfers
这个方法作为一个过度,单独对odex化做了一些处理,进而将参数进一步传入了AddSplitTransfers
def AddTransfer(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id,split=False):
......
  # Add the transfer(s).
  AddSplitTransfers(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id)
这里面的参数style就是第一步传入的"diff" by_id=self.transfer=[],接下来我们分析下这个方法

 def AddSplitTransfers(tgt_name, src_name, tgt_ranges, src_ranges,
      #声明分割个数,首次为0
      pieces = 0
              #拿到cache的大小
      cache_size = common.OPTIONS.cache_size
      split_threshold = 0.125
      #定义单个分割大小,以cache的8分之1除以定义的blockize4096
      max_blocks_per_transfer = int(cache_size * split_threshold /
                                    self.tgt.blocksize)

      # Change nothing for small files.
      #针对较小的文件,如果tgt和src区间大小都不够单个分割大小,则不做处理,直接生成Transfer对象
      if (tgt_ranges.size() <= max_blocks_per_transfer and
          src_ranges.size() <= max_blocks_per_transfer):

        Transfer(tgt_name, src_name, tgt_ranges, src_ranges,
                 self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),
                 style, by_id)
        return
#如果tgt和src的区间大于分割比例,将进入一个while循环,
      while (tgt_ranges.size() > max_blocks_per_transfer and src_ranges.size() > max_blocks_per_transfer):
        #这里的语法是字段拼接的意思,最终生成的split_name为tgt_name-pieces,例如第一次进while循环是/system/app/a.apk-0
        tgt_split_name = "%s-%d" % (tgt_name, pieces)
        src_split_name = "%s-%d" % (src_name, pieces)
        #取出区间中的分割所占的区间范围
        tgt_first = tgt_ranges.first(max_blocks_per_transfer)
        src_first = src_ranges.first(max_blocks_per_transfer)
        #传入生成Transfer对象
        Transfer(tgt_split_name, src_split_name, tgt_first, src_first,
                 self.tgt.RangeSha1(tgt_first), self.src.RangeSha1(src_first),
                 style, by_id)
        #去除tgt_ranges tgt_first,剩余的继续在while中分割
        tgt_ranges = tgt_ranges.subtract(tgt_first)
        src_ranges = src_ranges.subtract(src_first)
        #因为还在循环中,我们并不知道还会分割多少次,所以这里加1,之后生成的split_name就是/system/app/a.apk-1
        pieces += 1
      #这里代码的意思是,while循环走完后,剩余的部分需要处理,例如/system/app/a.apk-1
      if tgt_ranges.size() or src_ranges.size():
        # Must be both non-empty.
        assert tgt_ranges.size() and src_ranges.size()
        tgt_split_name = "%s-%d" % (tgt_name, pieces)
        src_split_name = "%s-%d" % (src_name, pieces)
        Transfer(tgt_split_name, src_split_name, tgt_ranges, src_ranges,
                 self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),
                 style, by_id)
生成Transfer对象
走完FindTransfer后进入class Transfer(object):的inti程序
  def __init__(self, tgt_name, src_name, tgt_ranges, src_ranges, tgt_sha1,
               src_sha1, style, by_id):
    #将我们传入的tgt_name,src_name,tgt_ranges,src_ranges,tgt_sha1,src_sha1
    self.tgt_name = tgt_name
    self.src_name = src_name
    self.tgt_ranges = tgt_ranges
    self.src_ranges = src_ranges
    self.tgt_sha1 = tgt_sha1
    self.src_sha1 = src_sha1
    self.style = style
    self.intact = (getattr(tgt_ranges, "monotonic", False) and
                   getattr(src_ranges, "monotonic", False))
    #print(getattr(tgt_ranges, "monotonic", False))
    # We use OrderedDict rather than dict so that the output is repeatable;
    # otherwise it would depend on the hash values of the Transfer objects.
    #声明两个字典,装载这两本字典将是下个方法做的事情
    self.goes_before = OrderedDict()
    self.goes_after = OrderedDict()
    self.stash_before = []
    self.use_stash = []
    #这里的by_id其实就是之前的self.transfer,这里记录的id其实就是当前self.Transfer的长度
    self.id = len(by_id)
    #把当前的Transfer对象放入self.transfer列表中,其中他的位置其实就是他的id号,这个参数将用到下个方法中,用于取出diff
    by_id.append(self)
5.GenerateDigraph(self):方法分析 这个方法中最主要的是包含了两个for循环,以下的流程中将引入一些例子
print("Generating digraph...")
   #声明一个空的列表
   source_ranges = []
    #遍历self.transfer集合,取出所有的Transfer对象,b=0: <130771-130825 diff to 32629-32683>  如果直接打印Transer对象,会出现这样的字符串
    因为在初始化Transer时添加了一个string方法,按这种格式进行输出
    for b in self.transfers:
      #遍历Transfer对象的src_range区间,这里拿到的s,e是区间的开头和结尾
      for s, e in b.src_ranges:
        #如果e大于source_range列表的长度,
        if e > len(source_ranges):
          #那么我们会将该长度追加到source_range,所以循环走完后,其实这个列表的长度就是e的长度,也就是system.map中最大的block值
          source_ranges.extend([None] * (e-len(source_ranges)))
        #遍历range(s,e),注意,这里已经变成了普通的range列表,这时候会取出列表中所有的元素range(130771,130825)
        for i in range(s, e):
          #如果source_ranges i位置上是空的
          if source_ranges[i] is None:
            #那么我们在i位置上放入一本字典,OrderedDict.fromkeys([b])是有序字典的写法,会生成一本字典key为含有b的列表,values为None
            #那么这个时候source_ranges 130771-130824位置上放入的就是b这个Transfer对象
            source_ranges[i] = OrderedDict.fromkeys([b])
          else:
            source_ranges[i][b] = None
其实这里第一步就是为了生成source_ranges这个列表,根据我们上面的例子,可以发现,列表的长度就是system.map最大的block值,而
每个位置上放入的其实是一本字典,而这个Transfer对象中,src_range的区间大小,就是该对象在source_ranges列表中的个数,注意,以上循环中使用的
是src_range.这样我们就按基本版本的block值,完完全全按顺序放在了列表中,
下面分析第二个for循环,取出小于基础版本最大区间目标版本中的Transfer对象
#遍历self.Transfer,取出Transer对象    
for a in self.transfers:
      #声明一个新的有序字典,intersections 
      intersections = OrderedDict()
      #遍历tgt_ranges,注意使用的是tgt_ranges,上个循环使用的是src_ranges
      for s, e in a.tgt_ranges:
        #用tgt_ranges的区间生成一个新的range列表
        for i in range(s, e):
          #如果i的值大于我们之前的source_ranges,将中断整个循环,为什么会这样呢,因为如果i>source_range,那说明操过了src_range的最大值.                    
          #也就不再处理了
          if i >= len(source_ranges): break
          # Add all the Transfers in source_ranges[i] to the (ordered) set.
          #如果source_ranger[i]不为空,也就是说该位置上有Transfer对象,
          if source_ranges[i] is not None:
            #这里要注意下,首先遍历的对象是source_ranges[i],也就是对应i位置上的元素,一个字典,我们遍历字典,其实j是取出的Transfer对象
            for j in source_ranges[i]:
              #这是字典的赋值方法,该字典key为j ,values为None,有序字典是有去重功能的,所以如果遍历ranges(s,e)存在相同的元素,这里只会出现一个
              #如果是不同的,都会放进去
              intersections[j] = None
最终生成gose_before,goes_after两个字典
    #注意这个for循环是是在for a in self.transfers:中的,并且与for s, e in a.tgt_ranges:同一个缩进的,
    #遍历intersections这个字典,实际打印发现有intersections=()的情况,那就说明tgt_range完全超出了src_range_max
    print("intersections================")
    print(intersections)
    for b in intersections:
        #如果a与b相同,那么中断当前的循环,继续等待下一个b的进入,进行对比
        if a is b: continue
        # If the blocks written by A are read by B, then B needs to go before A.
        #如果a这个Transer对象与b不同,那么取出a对象的tgt_ranges和b对象的src_ranges的intersect,也就是交集
        i = a.tgt_ranges.intersect(b.src_ranges)
        if i:
          if b.src_name == "__ZERO":
            # the cost of removing source blocks for the __ZERO domain
            # is (nearly) zero.
            size = 0
          else:
            #取出交集的长度size,也就是基础版本和升级版本相交的block
            size = i.size()
          #装载b的gose_before对象,内容为{a,size}
          b.goes_before[a] = size
          #装载a的goes_after对象,内容为{a,size}
          a.goes_after[b] = size
          这里好像看起来很乱,字面意思仿佛就是是谁杀了我,而我又杀了谁,下面我们以一个简单的例子进行分析
关于生成gose_before,gose_after
a.Tansfer
src_name = a.apk
tgt_name = a.apk
src_range = 0-1000
tgt_range = 500-2000

b.Transfer
src_name = b.apk
tgt_name = b.apk
src_range = 1001-2000
tgt_range = 2001-3000

i = a.tgt_ranges.intersect(b.src_ranges)
这时候用a的tgt_ranges 与 b.src_ranges 取交集,很明显可以取到500-2000
那么b的gose_before中存储了与其src_range有交集的a的tgt_range
b.goes_before[a] = size
那么a的goes_after中存储了与其tgt_range有交集的b的src_range
a.goes_after[b] = size
那么可能也会有c和d,
b的goes_after中存储了与其tgt_range有交集的c的src_range
b.gose_after[c] = size
a的gose_before中存储了与其src_range有交集的d的tgt_range
a.goes_before[d] = size
这样我们相当于会取出与当前APK基础版本的交集和升级版本的交集区间diff,以用于下一步的操作,这一部分的代码参数很多,我也是打印了很多的log
才慢慢看出了生成文件的作用.下一步将去操作这两个字典中存储的diff.

 

Android A/B System OTA分析(六)如何获取 payload 的 offset 和 size 1. 从 Android 8.0 (O) 开始,制作升级包时会同时将 offset 和 size 信息输出到 META-INF/com/android/metadata 文件中。具体的分析请参考 2.2 节。3. 使用这里提供的 python3 工具计算 update.zip 包内各数据的 offset 和 size 信息具体的代码和样例输出,参考 2.3 节。 阅读详情

相关推荐

Android5.0的更新包中system.new.dat文件的打包

Android 5.0开始,ota包采用了一种新的system.img的压缩方式。对于这种方式,如果是完整地升级,在ota的zip包中放的基本上就是一个ext4格式的image,但是它不是完整的ext4镜像,而是一个扣除了很多全零信息的包。如果是针对一个基础镜像做差值得到的差量包,也可以用这种方式进行稀疏描述。 这里主要介绍如何从system的源目录生成最后的ota的zip包。对于从ota包中解

howellzhu的专栏 2万+

Android9.0制作差分包报错

在新平台尝试制作差分包时报错,如下: ERROR: Failed to generate imgdiff for //system/priv-app/Test/Test.apk: tgt=70499-75692 75694-75930, src=70499-71112: W/ziparchive(25145): Zip: bad offsets (dir 22384640, size 53154, eocd 22245282) imgdiff E 07-14 14:21:25 25145 25145 img

BrainZou的博客 1399

Android Fota(差分包)制作

Android Fota包制作Fota包生成步骤问题 Fota包生成步骤 在Android系统编译(make)完成后,执行 make otapackage即可生成制作差分包所需的target package。 target包路径:android/out/target/product/msm_xxxx/target_files-package.zip 解压target_files-package.zip后,会看到一个ota_target_files.zip,这是制作Fota包需要的ota包。 Fota包是利用

arrol1786936883的博客 2079

Android9.0 制作差分包报错

ota做差分包的时候遇到imgdiff E 07-14 14:21:25 25145 25145 imgdiff.cpp:661] Failed to open zip file /tmp/tgt-KxNSNW: Invalid offset,导致生成差分包在内置应用做差分的时候会报错

Y在想什麽的博客 711

六、Android recovery升级断电保护机制

本篇将具体分析执行写入的流程,,整理完该流程后,将通过升级的log具体分析断电后重新升级的情况 一.如何断电后重启继续进入升级 几句话总结 1.调用framwork接口时会将command写入misc分区 2.升级过程中加入retry标志位,如果第二次升级,会将retry++ 3.misc分区的标志位只有在finlsh_recovery方法中,即使升级过程中掉电,重新上电后,mis...

李标标的博客 5052

imgdiff-比世界上最快的逐像素图像差异工具快。-Golang开发

imgdiff并不像这样的工具快,我也不为它感到骄傲,但是它比世界上最快的逐像素图像差异工具快3倍,因此也许您会发现它有用。 imgdiff比世界上最快的逐像素图像差异工具快。 为什么? imgdiff并不像这样的工具快,我也不为它感到骄傲,但是它比世界上最快的逐像素图像差异工具快3倍,因此也许您会发现它有用。 功能它可以做odiff可以做的所有事情。 快点。 基准赛普拉斯图像3446 x 10728命令平均值[s]最小值[s]最大值[s]相对imgdiff图像/cypress-1.png图像/cypress-2.png output.png 1.442±0

Android9.0 vendor分区整包升级

1、涉及文件 2、流程解析 3、问题探讨 1、涉及文件 ./build/make/core/Makefile ota脚本 ./build/toos/releasetools/ 9.0FOTA包 9.0源码 2、流程解析 本次问题是一家方案公司的编译环境有问题,经常出现升级报错vendor不匹配的问题,大致看了下升级脚本 修改为整包升级或者不升级两种情况, 首先...

李标标的博客 5786

blockimgdiff.py

解决Android9.0制作差分包时imgdiff 差分apk时报错,Failed to generate imgdiff for ...... imgdiff.cpp:661] Failed to open zip file /tmp/tgt-KxNSNW: Invalid offset. 解决方案是在imgdiff算法报错时,使用bsdiff算法去对apk做差分。 来自@qq_36071820

RK3326平台Android9.0OTA差分包制作

OTA差分包制作

Cailand的专栏 2749

手把手教你用TLP350驱动IGBT:光耦隔离驱动电路完整设计教程

本文详细介绍了如何使用TLP350光耦隔离驱动电路设计IGBT驱动方案。从TLP350芯片特性分析到驱动电路架构设计,包括信号调理、自举电源和栅极控制网络,再到PCB布局优化和保护电路设计,提供了完整的实战指南。通过实测波形分析和故障排查,帮助工程师快速掌握IGBT驱动电路的设计要点和进阶技巧。

weixin_30566063的博客 825

STM32 I²C外设深度解析:时序配置、主从模式与可靠性工程

I²C是一种广泛应用于嵌入式系统的同步串行总线协议,其核心在于精确的时序控制与可靠的电气特性匹配。理解SCL/SDA信号建立/保持时间、上升下降沿约束及硬件滤波机制,是保障通信稳定的基础;STM32通过I2C_TIMINGR寄存器实现对PRESC、SCLH、SCLL、SDADEL和SCLDEL等关键参数的精细调控,直接影响tSU:DAT、tVD:DAT等标准时序合规性;该能力支撑工业级高可靠场景下的主从双向通信、时钟拉伸管理与错误恢复;本文聚焦STM32平台,结合寄存器级配置、状态机行为与实战调试经验,系统

weixin_42146230的博客 87

【源码阅读系列】(专)OTA升级机制

本文对OTA升级的几种方式的概念和运用进行总结,仅在使用层面对其解释。

xuanyulevel6的博客 4662

从临床试验到互联网AB测试:边缘结构模型(MSM)如何解决‘时依性混杂’这个老大难问题

本文探讨边缘结构模型(MSM)如何解决时依性混杂问题,适用于临床试验和互联网AB测试。通过逆概率加权(IPTW)等方法,MSM能有效剥离混杂效应,准确评估长期干预效果。文章结合跨领域案例,详细解析MSM的核心原理、实施步骤及在推荐算法效果评估中的实战应用,帮助读者掌握这一先进因果推断工具。

weixin_28725949的博客 207

Android recovery分析(一)---全量升级包的编译流程

一、前言recovery的最主要功能就是升级,而升级文件就是升级包了,那么升级包时如何编译出来的呢?文就这个问题做个简要的分析。 注:本文中的叙述纯属个人理解,欢迎批评指正。二、升级包编译命令1.source build/envsetup.sh 2.lunch (选择合适的配置) 3.make otapackage 注:有些平台可能没有将“recoveryimage”、“bootima

mrcc_yy的博客 1万+

树莓派教程(基于python编程)--入门篇

一:格式化SD卡 SD卡插入读卡器连接电脑,使用SDFormatter对SD卡进行格式化 (重装烧录也要进行着SD卡格式化操作) 二:下载官方镜像 http://downloads.raspberrypi.org/raspbian_latest 三:烧录SD卡 使用Win32DiskImager将镜像烧录到格式化后的SD卡 SD卡盘根目录(/boot)下新建一个命名为ssh的...

qq_42109746的博客 6万+

无刷电机FOC控制入门:磁场、洛伦兹力与坐标转换详解

本文深入解析无刷电机FOC(磁场定向控制)的核心原理,从电磁学基础的磁场与洛伦兹力出发,详细阐述了如何通过Clarke变换、Park变换等坐标转换技术,将三相交流解耦为直流量进行控制,最终利用SVPWM技术实现平滑、高效的电机驱动。文章旨在帮助开发者理解FOC的物理本质与数学框架,为机器人、无人机等高性能应用奠定基础。

weixin_29306011的博客 267

Android 差分包制作流程分析

整包与差分包生成流程 差分包生成指令 makeotapackage将编译生成的(xxx项目为例) out/target/product/xxxxxxx/full_xxx_hxxxx-target_files-1527715386.zip 此时生成的是base.zip包 在代码中做一些修改,产生一些差异,第二次makeotapackage将编译生成的 out/target...

一杯咖啡的专栏 1万+

使用LSTM实现自动调制识别(RML2018.01a)

目录一、LSTM的代码实现1.1 导入包1.2 数据读取及数据集划分1.3 数据预处理1.4 构建LSTM模型 本文基于RML2018.01a大型数据集,给出LSTM的信号识别的实现方式(附代码) RML2018.01a数据集信息如下: 模型 传送门 CNN/ResNet 使用CNN/ResNet实现自动调制识别(RML2018.01a) LSTM 使用LSTM实现自动调制识别(RML2018.01a)) 一、LSTM的代码实现 1.1 导入包 import numpy as np

QAQIknow的博客 6123

OTA 知识点整理从添加分区到界面设计

OTA 知识点整理 这是建立在有完整的recovery 模式下面的添加。 1、OTA之分区添加 添加 misc 分区为例,把misc 换成 data system boot modem 等分区同样适用 1)先去配置添加分区的挂载 一般在: device/项目名/平台名/conf/fssrab.recovery.项目名 /dev/block/platform/****.dwm...

Venus 的博客 2481

【批处理脚本】-0.1-如何运行批处理bat文件

如何运行批处理bat文件

「汽车电子助手」的博客 1585

关于112G和56G PAM4信号的有效回损(ERL)

本文探讨了回波损耗(RL)对高速数字通信系统性能的影响,重点分析了时域信道运行裕度(COM)与频域回波损耗指标的关联性。研究表明,传统频域回波损耗指标在预测系统性能时存在假阳性/假阴性问题,特别是对PAM-4信号系统影响更为显著。通过引入基于时域脉冲响应的有效回波损耗(ERL)指标,并结合判决反馈均衡器(DFE)的补偿效应,可更准确评估系统性能。实验表明,短封装器件虽然频域回波损耗较差,但在实际系统中表现更优。研究提出通过时域门控加权等方法优化ERL计算,为高速互连设计提供了更可靠的性能评估框架。

小孟同学的博客 292

fota升级总结

一、[FAQ12481]Recovery mode在cache/recovery目录下新建一支文件,重启后,新建文件消失了 [DESCRIPTION] 1、在recovery.cpp文件的最后新建一支文件 /cache/recovery/wetest;并写入内容:welcome to recovery mode! 2、编译版本、烧写手机,开机后,进行恢复出厂设置;等手机进入自动重启状,...

sunj2007的专栏 4522

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

miui patchrom项目 生成原厂包/java内存溢出解决方法

修改common.py 修改后如下 # Copyright (C) 2008 The Android Open Source Project## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with...

weixin_30551947的博客 280

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

安卓edify脚本语法内建函数 updater_script脚本分析

OTA更新包 系统从bootable/recovery/updater编译更新程序二进制文件并将其用于 OTA 更新包。 更新包本身是包含可执行二进制文件META-INF/com/google/android/update-binary的 .zip 文件(ota_update.zip、incremental_ota_update.zip)。 更新程序包含多个内建函数和一个可扩展脚本语言 (E...

编程浅谈 1860

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

上一篇: 三年Android技术支持工作总结及心得
下一篇: MTK Recovery image的OTA升级过程
李标标
博客等级 码龄11年 124粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值