软件综合

Intelligence given, machines smarter.

今日: 2 主题: 486 回复: 4794版主:phpskycn
- 以上是置顶 -

2006版,来自网络,包含以下几卷: 第一卷:基础架构 第二卷:指令集参考(A-M/N-Z) 第三卷:系统编程指南(Part1/2) 附卷:优化参考 注意2006版已经包括了32位和64位架构,只不过64位架构被称为IA-32e。 Intel开发手册2006.rar 10.1M 36次 2001版,来自网络,包含以下几卷: 第一卷:基础架构 第二卷:指令集参考 第三卷:系统编程指南 注意2001版只更新到了奔腾4,也就是最高只有32位架构,并且扩展指令集只到SSE2.0。 Intel开发手册2001.zip 7.79M 3次 1999版,来自网络,包含以下几卷: 第一卷:基础架构 第二卷:指令集参考 第三卷:系统编程指南 注意1999版只更新到了奔腾III,也就是最高只有32位架构,并且扩展指令集只到SSE1.0。 intel开发手册1999.zip 7.64M 2次 目前找到的中文翻译版,来自网络,均为第三卷部分章节翻译: IA-32卷3:系统编程指南.pdf 1.55M 5次 INTEL开发手册卷3(中文版).pdf 1.99M 7次 现在的版本可以在这里下载: https://software.intel.com/en-us/articles/intel-sdm


Bitcoin: A Peer-to-Peer Electronic Cash System 中本聪(Satoshi Nakamoto) 原文: www.bitcoin.org ,翻译: www.8btc.com [摘要]:本文提出了一种完全通过点对点技术实现的电子现金系统,它使得在线支付能够直接由一方发起并支付给另外一方,中间不需要通过任何的金融机构。虽然数字签名(Digital signatures)部分解决了这个问题,但是如果仍然需要第三方的支持才能防止双重支付(double-spending)的话,那么这种系统也就失去了存在的价值。我们(we)在此提出一种解决方案,使现金系统在点对点的环境下运行,并防止双重支付问题。该网络通过随机散列(hashing)对全部交易加上时间戳(timestamps),将它们合并入一个不断延伸的基于随机散列的工作量证明(proof-of-work)的链条作为交易记录,除非重新完成全部的工作量证明,形成的交易记录将不可更改。最长的链条不仅将作为被观察到的事件序列(sequence)的证明,而且被看做是来自CPU计算能力最大的池(pool)。只要大多数的CPU计算能力都没有打算合作起来对全网进行攻击,那么诚实的节点将会生成最长的、超过攻击者的链条。这个系统本身需要的基础设施非常少。信息尽最大努力在全网传播即可,节点(nodes)可以随时离开和重新加入网络,并将最长的工作量证明链条作为在该节点离线期间发生的交易的证明。 1. 引言 互联网上的贸易,几乎都需要借助金融机构作为可资信赖的第三方来处理电子支付信息。虽然在绝大多数情况下这类系统都运作良好,但是这类系统仍然内生性地受制于“基于信用的模式”(trust based model)的弱点。人们无法实现完全不可逆的交易,因为金融机构总是不可避免地会出面协调争端。而金融中介的存在,也会增加交易的成本,并且限制了实际可行的最小交易规模,也限制了日常的小额支付交易。并且潜在的损失还在于,很多商品和服务本身是无法退货的,如果缺乏不可逆的支付手段,互联网的贸易就大大受限。因为有潜在的退款的可能,就需要交易双方拥有信任。此外,因为商家也必须对自己的客户小心提防,所以会向客户索取完全不必要的个人信息。而实际的商业行为中,一定比例的欺诈性客户也被认为是不可避免的,相关损失视作销售费用处理。而在使用物理现金的情况下,因为此时没有第三方信用中介的存在,这些销售费用和支付问题上的不确定性却是可以避免的。 所以,我们非常需要这样一种电子支付系统,它基于密码学原理而不基于信用,使得任何达成一致的双方,能够直接进行支付,从而不需要第三方中介的参与。杜绝回滚(reverse)支付交易的可能,这就可以保护特定的卖家免于欺诈;而对于想要保护买家的人来说,在此环境下设立通常的第三方担保机制也可谓轻松加愉快。在这篇论文中,我们(we)将提出一种通过点对点分布式的时间戳服务器来生成依照时间前后排列并加以记录的电子交易证明,从而解决双重支付问题。只要诚实的节点所控制的计算能力的总和,大于有合作关系的(cooperating)攻击者的计算能力的总和,该系统就是安全的。 2. 交易(Transactions) 我们定义,一枚电子货币(an electronic coin)是这样的一串数字签名:每一位所有者通过对前一次交易和下一位拥有者的公钥(Public key) 签署一个随机散列的数字签名,并将这个签名附加在这枚电子货币的末尾,电子货币就发送给了下一位所有者。而收款人通过对签名进行检验,就能够验证该链条的所有者。 该过程的问题在于,收款人将难以检验,之前的某位所有者,是否对这枚电子货币进行了双重支付。通常的解决方案,就是引入信得过的第三方权威,或者类似于造币厂(mint)的机构,来对每一笔交易进行检验,以防止双重支付。在每一笔交易结束后,这枚电子货币就要被造币厂回收,而造币厂将发行一枚新的电子货币;而只有造币厂直接发行的电子货币,才算作有效,这样就能够防止双重支付。可是该解决方案的问题在于,整个货币系统的命运完全依赖于运作造币厂的公司,因为每一笔交易都要经过该造币厂的确认,而该造币厂就好比是一家银行。 我们需要收款人有某种方法,能够确保之前的所有者没有对更早发生的交易实施签名。从逻辑上看,为了达到目的,实际上我们需要关注的只是于本交易之前发生的交易,而不需要关注这笔交易发生之后是否会有双重支付的尝试。为了确保某一次交易是不存在的,那么唯一的方法就是获悉之前发生过的所有交易。在造币厂模型里面,造币厂获悉所有的交易,并且决定了交易完成的先后顺序。如果想要在电子系统中排除第三方中介机构,那么交易信息就应当被公开宣布(publicly announced)1,我们需要整个系统内的所有参与者,都有唯一公认的历史交易序列。收款人需要确保在交易期间绝大多数的节点都认同该交易是首次出现。 3. 时间戳服务器(Timestamp server) 本解决方案首先提出一个“时间戳服务器”。时间戳服务器通过对以区块(block)形式存在的一组数据实施随机散列而加上时间戳,并将该随机散列进行广播,就像在新闻或世界性新闻组网络(Usenet)的发帖一样2345。显然,该时间戳能够证实特定数据必然于某特定时刻是的确存在的,因为只有在该时刻存在了才能获取相应的随机散列值。每个时间戳应当将前一个时间戳纳入其随机散列值中,每一个随后的时间戳都对之前的一个时间戳进行增强(reinforcing),这样就形成了一个链条(Chain)。 4. 工作量证明(Proof-of-Work) 为了在点对点的基础上构建一组分散化的时间戳服务器,仅仅像报纸或世界性新闻网络组一样工作是不够的,我们还需要一个类似于亚当·柏克(Adam Back)提出的哈希现金(Hashcash)6。在进行随机散列运算时,工作量证明机制引入了对某一个特定值的扫描工作,比方说SHA-256下,随机散列值以一个或多个0开始。那么随着0的数目的上升, 找到这个解所需要的工作量将呈指数增长,但是检验结果仅需要一次随机散列运算。 我们在区块中补增一个随机数(Nonce),这个随机数要使得该给定区块的随机散列值出现了所需的那么多个0。我们通过反复尝试来找到这个随机数,找到为止。这样我们就构建了一个工作量证明机制。只要该CPU耗费的工作量能够满足该工作量证明机制,那么除非重新完成相当的工作量,该区块的信息就不可更改。由于之后的区块是链接在该区块之后的,所以想要更改该区块中的信息,就还需要重新完成之后所有区块的全部工作量。 同时,该工作量证明机制还解决了在集体投票表决时,谁是大多数的问题。如果决定大多数的方式是基于IP地址的,一IP地址一票,那么如果有人拥有分配大量IP地址的权力,则该机制就被破坏了。而工作量证明机制的本质则是一CPU一票。“大多数”的决定表达为最长的链,因为最长的链包含了最大的工作量。如果大多数的CPU为诚实的节点控制,那么诚实的链条将以最快的速度延长,并超越其他的竞争链条。如果想要对业已出现的区块进行修改,攻击者必须重新完成该区块的工作量外加该区块之后所有区块的工作量,并最终赶上和超越诚实节点的工作量。我们将在后文证明,设想一个较慢的攻击者试图赶上随后的区块,那么其成功概率将呈指数化递减。 另一个问题是,硬件的运算速度在高速增长,且节点参与网络的程度会有所起伏。为了解决这个问题,工作量证明的难度(the proof-of-work difficulty)将采用移动平均目标的方法来确定,即令难度指向令每小时生成区块的速度为某一预设的平均数。如果区块生成的速度过快,那么难度就会提高。 5. 网络 运行该网络的步骤如下: 1) 新的交易向全网进行广播; 2) 每一个节点都将收到的交易信息纳入一个区块中; 3) 每个节点都尝试在自己的区块中找到一个具有足够难度的工作量证明; 4) 当一个节点找到了一个工作量证明,它就向全网进行广播; 5) 当且仅当包含在该区块中的所有交易都是有效的且之前未存在过的,其他节点才认同该区块的有效性; 6) 其他节点表示他们接受该区块,而表示接受的方法,则是在跟随该区块的末尾,制造新的区块以延长该链条,而将被接受区块的随机散列值视为先于新区快的随机散列值。 节点始终都将最长的链条视为正确的链条,并持续工作和延长它。如果有两个节点同时广播不同版本的新区块,那么其他节点在接收到该区块的时间上将存在先后差别。当此情形,他们将在率先收到的区块基础上进行工作,但也会保留另外一个链条,以防后者变成最长的链条。该僵局(tie)的打破要等到下一个工作量证明被发现,而其中的一条链条被证实为是较长的一条,那么在另一条分支链条上工作的节点将转换阵营,开始在较长的链条上工作。 所谓“新的交易要广播”,实际上不需要抵达全部的节点。只要交易信息能够抵达足够多的节点,那么他们将很快被整合进一个区块中。而区块的广播对被丢弃的信息是具有容错能力的。如果一个节点没有收到某特定区块,那么该节点将会发现自己缺失了某个区块,也就可以提出自己下载该区块的请求。 6. 激励 我们约定如此:每个区块的第一笔交易进行特殊化处理,该交易产生一枚由该区块创造者拥有的新的电子货币。这样就增加了节点支持该网络的激励,并在没有中央集权机构发行货币的情况下,提供了一种将电子货币分配到流通领域的一种方法。这种将一定数量新货币持续增添到货币系统中的方法,非常类似于耗费资源去挖掘金矿并将黄金注入到流通领域。此时,CPU的时间和电力消耗就是消耗的资源。 另外一个激励的来源则是交易费(transaction fees)。如果某笔交易的输出值小于输入值,那么差额就是交易费,该交易费将被增加到该区块的激励中。只要既定数量的电子货币已经进入流通,那么激励机制就可以逐渐转换为完全依靠交易费,那么本货币系统就能够免于通货膨胀。 激励系统也有助于鼓励节点保持诚实。如果有一个贪婪的攻击者能够调集比所有诚实节点加起来还要多的CPU计算力,那么他就面临一个选择:要么将其用于诚实工作产生新的电子货币,或者将其用于进行二次支付攻击。那么他就会发现,按照规则行事、诚实工作是更有利可图的。因为该等规则使得他能够拥有更多的电子货币,而不是破坏这个系统使得其自身财富的有效性受损。 7. 回收硬盘空间 如果最近的交易已经被纳入了足够多的区块之中,那么就可以丢弃该交易之前的数据,以回收硬盘空间。为了同时确保不损害区块的随机散列值,交易信息被随机散列时,被构建成一种Merkle树(Merkle tree)7的形态,使得只有根(root)被纳入了区块的随机散列值。通过将该树(tree)的分支拔除(stubbing)的方法,老区块就能被压缩。而内部的随机散列值是不必保存的。 不含交易信息的区块头(Block header)大小仅有80字节。如果我们设定区块生成的速率为每10分钟一个,那么每一年产生的数据位4.2MB。(80 bytes * 6 * 24 * 365 = 4.2MB)。2008年,PC系统通常的内存容量为2GB,按照摩尔定律的预言,即使将全部的区块头存储于内存之中都不是问题。 8. 简化的支付确认(Simplified Payment Verification) 在不运行完整网络节点的情况下,也能够对支付进行检验。一个用户需要保留最长的工作量证明链条的区块头的拷贝,它可以不断向网络发起询问,直到它确信自己拥有最长的链条,并能够通过merkle的分支通向它被加上时间戳并纳入区块的那次交易。节点想要自行检验该交易的有效性原本是不可能的,但通过追溯到链条的某个位置,它就能看到某个节点曾经接受过它,并且于其后追加的区块也进一步证明全网曾经接受了它。 当此情形,只要诚实的节点控制了网络,检验机制就是可靠的。但是,当全网被一个计算力占优的攻击者攻击时,将变得较为脆弱。因为网络节点能够自行确认交易的有效性,只要攻击者能够持续地保持计算力优势,简化的机制会被攻击者焊接的(fabricated)交易欺骗。那么一个可行的策略就是,只要他们发现了一个无效的区块,就立刻发出警报,收到警报的用户将立刻开始下载被警告有问题的区块或交易的完整信息,以便对信息的不一致进行判定。对于日常会发生大量收付的商业机构,可能仍会希望运行他们自己的完整节点,以保持较大的独立完全性和检验的快速性。 9. 价值的组合与分割(Combining and Splitting Value) 虽然可以单个单个地对电子货币进行处理,但是对于每一枚电子货币单独发起一次交易将是一种笨拙的办法。为了使得价值易于组合与分割,交易被设计为可以纳入多个输入和输出。一般而言是某次价值较大的前次交易构成的单一输入,或者由某几个价值较小的前次交易共同构成的并行输入,但是输出最多只有两个:一个用于支付,另一个用于找零(如有)。 需要指出的是,虽然一笔交易依赖于之前的多笔交易、这些交易又各自依赖于多笔交易,但是这并不存在任何问题。因为这个工作机制并不需要展开检验之前发生的所有交易历史。 10. 隐私(Privacy) 传统的造币厂模型为交易的参与者提供了一定程度的隐私保护,因为试图向可信任的第三方索取交易信息是严格受限的。但是如果将交易信息向全网进行广播,就意味着这样的方法失效了。但是隐私依然可以得到保护:将公钥保持为匿名。公众得知的信息仅仅是有某个人将一定数量的货币发所给了另外一个人,但是难以将该交易同某个特定的人联系在一起,也就是说,公众难以确信,这些人究竟是谁。这同股票交易所发布的信息是类似的,每一手股票买卖发生的时间、交易量是记录在案且可供查询的,但是交易双方的身份信息却不予透露。 作为额外的预防措施,使用者可以让每次交易都生成一个新的地址,以确保这些交易不被追溯到一个共同的所有者。不过由于存在并行输入,一定程度上的追溯还是不可避免的,因为并行输入暗示这些货币都属于同一个所有者。此时的风险在于,如果某个人的某一个公钥被确认属于他,那么就可以追溯出此人的其它很多交易。 11. 计算 设想如下场景:一个攻击者试图比诚实节点产生链条更快地制造替代性区块链。即便它达到了这一目的,但是整个系统也并非就此完全受制于攻击者的独断意志了,比方说凭空创造价值,或者掠夺本不属于攻击者的货币。这是因为节点将不会接受无效的交易,而诚实的节点永远不会接受一个包含了无效信息的区块。一个攻击者能做的,最多是更改他自己的交易信息,并试图拿回他刚刚付给别人的钱。 诚实链条和攻击者链条之间的竞赛,可以用二叉树随机漫步(Binomial Random Walk)来描述。成功事件定义为诚实链条延长了一个区块,使其领先性+1,而失败事件则是攻击者的链条被延长了一个区块,使得差距-1。 攻击者成功填补某一既定差距的可能性,可以近似地看做赌徒破产问题(Gambler’s Ruin problem)。假定一个赌徒拥有无限的透支信用,然后开始进行潜在次数为无穷的赌博,试图填补上自己的亏空。那么我们可以计算他填补上亏空的概率,也就是该攻击者赶上诚实链条,如下所示8: 其中, p:诚实节点制造出下一个节点的概率 q:攻击者制造出下一个节点的概率 qz:攻击者最终消弭了z个区块的落后差距 假定p>q,那么攻击成功的概率就因为区块数的增长而呈现指数化下降。由于概率是攻击者的敌人,如果他不能幸运且快速地获得成功,那么他获得成功的机会随着时间的流逝就变得愈发渺茫。那么我们考虑一个收款人需要等待多长时间,才能足够确信付款人已经难以更改交易了。我们假设付款人是一个支付攻击者,希望让收款人在一段时间内相信他已经付过款了,然后立即将支付的款项重新支付给自己。虽然收款人届时会发现这一点,但为时已晚。 收款人生成了新的一对密钥组合,然后只预留一个较短的时间将公钥发送给付款人。这将可以防止以下情况:付款人预先准备好一个区块链然后持续地对此区块进行运算,直到运气让他的区块链超越了诚实链条,方才立即执行支付。当此情形,只要交易一旦发出,攻击者就开始秘密地准备一条包含了该交易替代版本的平行链条。 然后收款人将等待交易出现在首个区块中,然后在等到z个区块链接其后。此时,他仍然不能确切知道攻击者已经进展了多少个区块,但是假设诚实区块将耗费平均预期时间以产生一个区块,那么攻击者的潜在进展就是一个泊松分布,分布的期望值为: λ=z(q/p) 当此情形,为了计算攻击者追赶上的概率,我们将攻击者取得进展区块数量的泊松分布的概率密度,乘以在该数量下攻击者依然能够追赶上的概率。 为避免对无限数列求和,化为如下形式: 写为如下C语言代码: #include <math.h> double AttackerSuccessProbability(double q, int z) { double p = 1.0 - q; double lambda = z * (q / p); double sum = 1.0; int i, k; for (k = 0; k <= z; k++) { double poisson = exp(-lambda); for (i = 1; i <= k; i++) poisson *= lambda / i; sum -= poisson * (1 - pow(q / p, z - k)); } return sum; } 对其进行运算,我们可以得到如下的概率结果,发现概率对z值呈指数下降。 当q=0.1时 z=0 P=1.0000000 z=1 P=0.2045873 z=2 P=0.0509779 z=3 P=0.0131722 z=4 P=0.0034552 z=5 P=0.0009137 z=6 P=0.0002428 z=7 P=0.0000647 z=8 P=0.0000173 z=9 P=0.0000046 z=10 P=0.0000012 当q=0.3时 z=0 P=1.0000000 z=5 P=0.1773523 z=10 P=0.0416605 z=15 P=0.0101008 z=20 P=0.0024804 z=25 P=0.0006132 z=30 P=0.0001522 z=35 P=0.0000379 z=40 P=0.0000095 z=45 P=0.0000024 z=50 P=0.0000006 求解令P<0.1%的z值: 为使P<0.001,则 q=0.10 z=5 q=0.15 z=8 q=0.20 z=11 q=0.25 z=15 q=0.30 z=24 q=0.35 z=41 q=0.40 z=89 q=0.45 z=340 12.结论 我们在此提出了一种不需要信用中介的电子支付系统。我们首先讨论了通常的电子货币的电子签名原理,虽然这种系统为所有权提供了强有力的控制,但是不足以防止双重支付。为了解决这个问题,我们提出了一种采用工作量证明机制的点对点网络来记录交易的公开信息,只要诚实的节点能够控制绝大多数的CPU计算能力,就能使得攻击者事实上难以改变交易记录。该网络的强健之处在于它结构上的简洁性。节点之间的工作大部分是彼此独立的,只需要很少的协同。每个节点都不需要明确自己的身份,由于交易信息的流动路径并无任何要求,所以只需要尽其最大努力传播即可。节点可以随时离开网络,而想重新加入网络也非常容易,因为只需要补充接收离开期间的工作量证明链条即可。节点通过自己的CPU计算力进行投票,表决他们对有效区块的确认,他们不断延长有效的区块链来表达自己的确认,并拒绝在无效的区块之后延长区块以表示拒绝。 本框架包含了一个P2P电子货币系统所需要的全部规则和激励措施。 参考文献 [1] W Dai(戴伟),a scheme for a group of untraceable digital pseudonyms to pay each other with money and to enforce contracts amongst themselves without outside help(一种能够借助电子假名在群体内部相互支付并迫使个体遵守规则且不需要外界协助的电子现金机制), “B-money”, http://www.weidai.com/bmoney.txt , 1998 [2] H. Massias, X.S. Avila, and J.-J. Quisquater, "Design of a secure timestamping service with minimal trust requirements,"(在最小化信任的基础上设计一种时间戳服务器) In 20th Symposium on Information Theory in the Benelux, May 1999. [3] S. Haber, W.S. Stornetta, "How to time-stamp a digital document," (怎样为电子文件添加时间戳)In Journal of Cryptology, vol 3, No.2, pages 99-111, 1991. [4] D. Bayer, S. Haber, W.S. Stornetta, "Improving the efficiency and reliability of digital time-stamping,"(提升电子时间戳的效率和可靠性) In Sequences II: Methods in Communication, Security and Computer Science, pages 329-334, 1993. [5] S. Haber, W.S. Stornetta, "Secure names for bit-strings,"(比特字串的安全命名) In Proceedings of the 4th ACM Conference on Computer and Communications Security, pages 28-35, April 1997. on Computer and Communications Security, pages 28-35, April,1997. [6] A. Back, "Hashcash - a denial of service counter-measure,"(哈希现金——拒绝服务式攻击的克制方法) http://www.hashcash.org/papers/hashcash.pdf , 2002. [7] R.C. Merkle, "Protocols for public key cryptosystems," (公钥密码系统的协议)In Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, pages 122-133, April 1980. [8] W. Feller, "An introduction to probability theory and its applications," (概率学理论与应用导论)1957 原文 bitcoin.pdf 180k

Bitcoin: A Peer-to-Peer Electronic Cash System 中本聪(Satoshi Nakamoto) 原文: www.bitcoin.org ,翻译: www.8btc.com [摘要]:本文提出了一种完全通过点对点技术实现的电子现金系统,它使得在线支付能够直接由一方发起并支付给另外一方,中间不需要通过任何的金融机构。虽然数字签名(Digital signatures)部分解决了这个问题,但是如果仍然需要第三方的支持才能防止双重支付(double-spending)的话,那么这种系统也就失去了存在的价值。我们(we)在此提出一种解决方案,使现金系统在点对点的环境下运行,并防止双重支付问题。该网络通过随机散列(hashing)对全部交易加上时间戳(timestamps),将它们合并入一个不断延伸的基于随机散列的工作量证明(proof-of-work)的链条作为交易记录,除非重新完成全部的工作量证明,形成的交易记录将不可更改。最长的链条不仅将作为被观察到的事件序列(sequence)的证明,而且被看做是来自CPU计算能力最大的池(pool)。只要大多数的CPU计算能力都没有打算合作起来对全网进行攻击,那么诚实的节点将会生成最长的、超过攻击者的链条。这个系统本身需要的基础设施非常少。信息尽最大努力在全网传播即可,节点(nodes)可以随时离开和重新加入网络,并将最长的工作量证明链条作为在该节点离线期间发生的交易的证明。 1. 引言 互联网上的贸易,几乎都需要借助金融机构作为可资信赖的第三方来处理电子支付信息。虽然在绝大多数情况下这类系统都运作良好,但是这类系统仍然内生性地受制于“基于信用的模式”(trust based model)的弱点。人们无法实现完全不可逆的交易,因为金融机构总是不可避免地会出面协调争端。而金融中介的存在,也会增加交易的成本,并且限制了实际可行的最小交易规模,也限制了日常的小额支付交易。并且潜在的损失还在于,很多商品和服务本身是无法退货的,如果缺乏不可逆的支付手段,互联网的贸易就大大受限。因为有潜在的退款的可能,就需要交易双方拥有信任。此外,因为商家也必须对自己的客户小心提防,所以会向客户索取完全不必要的个人信息。而实际的商业行为中,一定比例的欺诈性客户也被认为是不可避免的,相关损失视作销售费用处理。而在使用物理现金的情况下,因为此时没有第三方信用中介的存在,这些销售费用和支付问题上的不确定性却是可以避免的。 所以,我们非常需要这样一种电子支付系统,它基于密码学原理而不基于信用,使得任何达成一致的双方,能够直接进行支付,从而不需要第三方中介的参与。杜绝回滚(reverse)支付交易的可能,这就可以保护特定的卖家免于欺诈;而对于想要保护买家的人来说,在此环境下设立通常的第三方担保机制也可谓轻松加愉快。在这篇论文中,我们(we)将提出一种通过点对点分布式的时间戳服务器来生成依照时间前后排列并加以记录的电子交易证明,从而解决双重支付问题。只要诚实的节点所控制的计算能力的总和,大于有合作关系的(cooperating)攻击者的计算能力的总和,该系统就是安全的。 2. 交易(Transactions) 我们定义,一枚电子货币(an electronic coin)是这样的一串数字签名:每一位所有者通过对前一次交易和下一位拥有者的公钥(Public key) 签署一个随机散列的数字签名,并将这个签名附加在这枚电子货币的末尾,电子货币就发送给了下一位所有者。而收款人通过对签名进行检验,就能够验证该链条的所有者。 (附件:279414) 该过程的问题在于,收款人将难以检验,之前的某位所有者,是否对这枚电子货币进行了双重支付。通常的解决方案,就是引入信得过的第三方权威,或者类似于造币厂(mint)的机构,来对每一笔交易进行检验,以防止双重支付。在每一笔交易结束后,这枚电子货币就要被造币厂回收,而造币厂将发行一枚新的电子货币;而只有造币厂直接发行的电子货币,才算作有效,这样就能够防止双重支付。可是该解决方案的问题在于,整个货币系统的命运完全依赖于运作造币厂的公司,因为每一笔交易都要经过该造币厂的确认,而该造币厂就好比是一家银行。 我们需要收款人有某种方法,能够确保之前的所有者没有对更早发生的交易实施签名。从逻辑上看,为了达到目的,实际上我们需要关注的只是于本交易之前发生的交易,而不需要关注这笔交易发生之后是否会有双重支付的尝试。为了确保某一次交易是不存在的,那么唯一的方法就是获悉之前发生过的所有交易。在造币厂模型里面,造币厂获悉所有的交易,并且决定了交易完成的先后顺序。如果想要在电子系统中排除第三方中介机构,那么交易信息就应当被公开宣布(publicly announced)1,我们需要整个系统内的所有参与者,都有唯一公认的历史交易序列。收款人需要确保在交易期间绝大多数的节点都认同该交易是首次出现。 3. 时间戳服务器(Timestamp server) 本解决方案首先提出一个“时间戳服务器”。时间戳服务器通过对以区块(block)形式存在的一组数据实施随机散列而加上时间戳,并将该随机散列进行广播,就像在新闻或世界性新闻组网络(Usenet)的发帖一样2345。显然,该时间戳能够证实特定数据必然于某特定时刻是的确存在的,因为只有在该时刻存在了才能获取相应的随机散列值。每个时间戳应当将前一个时间戳纳入其随机散列值中,每一个随后的时间戳都对之前的一个时间戳进行增强(reinforcing),这样就形成了一个链条(Chain)。 (附件:279415) 4. 工作量证明(Proof-of-Work) 为了在点对点的基础上构建一组分散化的时间戳服务器,仅仅像报纸或世界性新闻网络组一样工作是不够的,我们还需要一个类似于亚当·柏克(Adam Back)提出的哈希现金(Hashcash)6。在进行随机散列运算时,工作量证明机制引入了对某一个特定值的扫描工作,比方说SHA-256下,随机散列值以一个或多个0开始。那么随着0的数目的上升, 找到这个解所需要的工作量将呈指数增长,但是检验结果仅需要一次随机散列运算。 我们在区块中补增一个随机数(Nonce),这个随机数要使得该给定区块的随机散列值出现了所需的那么多个0。我们通过反复尝试来找到这个随机数,找到为止。这样我们就构建了一个工作量证明机制。只要该CPU耗费的工作量能够满足该工作量证明机制,那么除非重新完成相当的工作量,该区块的信息就不可更改。由于之后的区块是链接在该区块之后的,所以想要更改该区块中的信息,就还需要重新完成之后所有区块的全部工作量。 (附件:279416) 同时,该工作量证明机制还解决了在集体投票表决时,谁是大多数的问题。如果决定大多数的方式是基于IP地址的,一IP地址一票,那么如果有人拥有分配大量IP地址的权力,则该机制就被破坏了。而工作量证明机制的本质则是一CPU一票。“大多数”的决定表达为最长的链,因为最长的链包含了最大的工作量。如果大多数的CPU为诚实的节点控制,那么诚实的链条将以最快的速度延长,并超越其他的竞争链条。如果想要对业已出现的区块进行修改,攻击者必须重新完成该区块的工作量外加该区块之后所有区块的工作量,并最终赶上和超越诚实节点的工作量。我们将在后文证明,设想一个较慢的攻击者试图赶上随后的区块,那么其成功概率将呈指数化递减。 另一个问题是,硬件的运算速度在高速增长,且节点参与网络的程度会有所起伏。为了解决这个问题,工作量证明的难度(the proof-of-work difficulty)将采用移动平均目标的方法来确定,即令难度指向令每小时生成区块的速度为某一预设的平均数。如果区块生成的速度过快,那么难度就会提高。 5. 网络 运行该网络的步骤如下: 1) 新的交易向全网进行广播; 2) 每一个节点都将收到的交易信息纳入一个区块中; 3) 每个节点都尝试在自己的区块中找到一个具有足够难度的工作量证明; 4) 当一个节点找到了一个工作量证明,它就向全网进行广播; 5) 当且仅当包含在该区块中的所有交易都是有效的且之前未存在过的,其他节点才认同该区块的有效性; 6) 其他节点表示他们接受该区块,而表示接受的方法,则是在跟随该区块的末尾,制造新的区块以延长该链条,而将被接受区块的随机散列值视为先于新区快的随机散列值。 节点始终都将最长的链条视为正确的链条,并持续工作和延长它。如果有两个节点同时广播不同版本的新区块,那么其他节点在接收到该区块的时间上将存在先后差别。当此情形,他们将在率先收到的区块基础上进行工作,但也会保留另外一个链条,以防后者变成最长的链条。该僵局(tie)的打破要等到下一个工作量证明被发现,而其中的一条链条被证实为是较长的一条,那么在另一条分支链条上工作的节点将转换阵营,开始在较长的链条上工作。 所谓“新的交易要广播”,实际上不需要抵达全部的节点。只要交易信息能够抵达足够多的节点,那么他们将很快被整合进一个区块中。而区块的广播对被丢弃的信息是具有容错能力的。如果一个节点没有收到某特定区块,那么该节点将会发现自己缺失了某个区块,也就可以提出自己下载该区块的请求。 6. 激励 我们约定如此:每个区块的第一笔交易进行特殊化处理,该交易产生一枚由该区块创造者拥有的新的电子货币。这样就增加了节点支持该网络的激励,并在没有中央集权机构发行货币的情况下,提供了一种将电子货币分配到流通领域的一种方法。这种将一定数量新货币持续增添到货币系统中的方法,非常类似于耗费资源去挖掘金矿并将黄金注入到流通领域。此时,CPU的时间和电力消耗就是消耗的资源。 另外一个激励的来源则是交易费(transaction fees)。如果某笔交易的输出值小于输入值,那么差额就是交易费,该交易费将被增加到该区块的激励中。只要既定数量的电子货币已经进入流通,那么激励机制就可以逐渐转换为完全依靠交易费,那么本货币系统就能够免于通货膨胀。 激励系统也有助于鼓励节点保持诚实。如果有一个贪婪的攻击者能够调集比所有诚实节点加起来还要多的CPU计算力,那么他就面临一个选择:要么将其用于诚实工作产生新的电子货币,或者将其用于进行二次支付攻击。那么他就会发现,按照规则行事、诚实工作是更有利可图的。因为该等规则使得他能够拥有更多的电子货币,而不是破坏这个系统使得其自身财富的有效性受损。 7. 回收硬盘空间 如果最近的交易已经被纳入了足够多的区块之中,那么就可以丢弃该交易之前的数据,以回收硬盘空间。为了同时确保不损害区块的随机散列值,交易信息被随机散列时,被构建成一种Merkle树(Merkle tree)7的形态,使得只有根(root)被纳入了区块的随机散列值。通过将该树(tree)的分支拔除(stubbing)的方法,老区块就能被压缩。而内部的随机散列值是不必保存的。 (附件:279417) 不含交易信息的区块头(Block header)大小仅有80字节。如果我们设定区块生成的速率为每10分钟一个,那么每一年产生的数据位4.2MB。(80 bytes * 6 * 24 * 365 = 4.2MB)。2008年,PC系统通常的内存容量为2GB,按照摩尔定律的预言,即使将全部的区块头存储于内存之中都不是问题。 8. 简化的支付确认(Simplified Payment Verification) 在不运行完整网络节点的情况下,也能够对支付进行检验。一个用户需要保留最长的工作量证明链条的区块头的拷贝,它可以不断向网络发起询问,直到它确信自己拥有最长的链条,并能够通过merkle的分支通向它被加上时间戳并纳入区块的那次交易。节点想要自行检验该交易的有效性原本是不可能的,但通过追溯到链条的某个位置,它就能看到某个节点曾经接受过它,并且于其后追加的区块也进一步证明全网曾经接受了它。 (附件:279418) 当此情形,只要诚实的节点控制了网络,检验机制就是可靠的。但是,当全网被一个计算力占优的攻击者攻击时,将变得较为脆弱。因为网络节点能够自行确认交易的有效性,只要攻击者能够持续地保持计算力优势,简化的机制会被攻击者焊接的(fabricated)交易欺骗。那么一个可行的策略就是,只要他们发现了一个无效的区块,就立刻发出警报,收到警报的用户将立刻开始下载被警告有问题的区块或交易的完整信息,以便对信息的不一致进行判定。对于日常会发生大量收付的商业机构,可能仍会希望运行他们自己的完整节点,以保持较大的独立完全性和检验的快速性。 (附件:279419) 9. 价值的组合与分割(Combining and Splitting Value) 虽然可以单个单个地对电子货币进行处理,但是对于每一枚电子货币单独发起一次交易将是一种笨拙的办法。为了使得价值易于组合与分割,交易被设计为可以纳入多个输入和输出。一般而言是某次价值较大的前次交易构成的单一输入,或者由某几个价值较小的前次交易共同构成的并行输入,但是输出最多只有两个:一个用于支付,另一个用于找零(如有)。 需要指出的是,虽然一笔交易依赖于之前的多笔交易、这些交易又各自依赖于多笔交易,但是这并不存在任何问题。因为这个工作机制并不需要展开检验之前发生的所有交易历史。 10. 隐私(Privacy) (附件:279420) 传统的造币厂模型为交易的参与者提供了一定程度的隐私保护,因为试图向可信任的第三方索取交易信息是严格受限的。但是如果将交易信息向全网进行广播,就意味着这样的方法失效了。但是隐私依然可以得到保护:将公钥保持为匿名。公众得知的信息仅仅是有某个人将一定数量的货币发所给了另外一个人,但是难以将该交易同某个特定的人联系在一起,也就是说,公众难以确信,这些人究竟是谁。这同股票交易所发布的信息是类似的,每一手股票买卖发生的时间、交易量是记录在案且可供查询的,但是交易双方的身份信息却不予透露。 作为额外的预防措施,使用者可以让每次交易都生成一个新的地址,以确保这些交易不被追溯到一个共同的所有者。不过由于存在并行输入,一定程度上的追溯还是不可避免的,因为并行输入暗示这些货币都属于同一个所有者。此时的风险在于,如果某个人的某一个公钥被确认属于他,那么就可以追溯出此人的其它很多交易。 11. 计算 设想如下场景:一个攻击者试图比诚实节点产生链条更快地制造替代性区块链。即便它达到了这一目的,但是整个系统也并非就此完全受制于攻击者的独断意志了,比方说凭空创造价值,或者掠夺本不属于攻击者的货币。这是因为节点将不会接受无效的交易,而诚实的节点永远不会接受一个包含了无效信息的区块。一个攻击者能做的,最多是更改他自己的交易信息,并试图拿回他刚刚付给别人的钱。 诚实链条和攻击者链条之间的竞赛,可以用二叉树随机漫步(Binomial Random Walk)来描述。成功事件定义为诚实链条延长了一个区块,使其领先性+1,而失败事件则是攻击者的链条被延长了一个区块,使得差距-1。 攻击者成功填补某一既定差距的可能性,可以近似地看做赌徒破产问题(Gambler’s Ruin problem)。假定一个赌徒拥有无限的透支信用,然后开始进行潜在次数为无穷的赌博,试图填补上自己的亏空。那么我们可以计算他填补上亏空的概率,也就是该攻击者赶上诚实链条,如下所示8: (附件:279421) 其中, p:诚实节点制造出下一个节点的概率 q:攻击者制造出下一个节点的概率 qz:攻击者最终消弭了z个区块的落后差距 假定p>q,那么攻击成功的概率就因为区块数的增长而呈现指数化下降。由于概率是攻击者的敌人,如果他不能幸运且快速地获得成功,那么他获得成功的机会随着时间的流逝就变得愈发渺茫。那么我们考虑一个收款人需要等待多长时间,才能足够确信付款人已经难以更改交易了。我们假设付款人是一个支付攻击者,希望让收款人在一段时间内相信他已经付过款了,然后立即将支付的款项重新支付给自己。虽然收款人届时会发现这一点,但为时已晚。 收款人生成了新的一对密钥组合,然后只预留一个较短的时间将公钥发送给付款人。这将可以防止以下情况:付款人预先准备好一个区块链然后持续地对此区块进行运算,直到运气让他的区块链超越了诚实链条,方才立即执行支付。当此情形,只要交易一旦发出,攻击者就开始秘密地准备一条包含了该交易替代版本的平行链条。 然后收款人将等待交易出现在首个区块中,然后在等到z个区块链接其后。此时,他仍然不能确切知道攻击者已经进展了多少个区块,但是假设诚实区块将耗费平均预期时间以产生一个区块,那么攻击者的潜在进展就是一个泊松分布,分布的期望值为: λ=z(q/p) 当此情形,为了计算攻击者追赶上的概率,我们将攻击者取得进展区块数量的泊松分布的概率密度,乘以在该数量下攻击者依然能够追赶上的概率。 (附件:279422) 为避免对无限数列求和,化为如下形式: (附件:279423) 写为如下C语言代码: #include <math.h> double AttackerSuccessProbability(double q, int z) { double p = 1.0 - q; double lambda = z * (q / p); double sum = 1.0; int i, k; for (k = 0; k <= z; k++) { double poisson = exp(-lambda); for (i = 1; i <= k; i++) poisson *= lambda / i; sum -= poisson * (1 - pow(q / p, z - k)); } return sum; } 对其进行运算,我们可以得到如下的概率结果,发现概率对z值呈指数下降。 当q=0.1时 z=0 P=1.0000000 z=1 P=0.2045873 z=2 P=0.0509779 z=3 P=0.0131722 z=4 P=0.0034552 z=5 P=0.0009137 z=6 P=0.0002428 z=7 P=0.0000647 z=8 P=0.0000173 z=9 P=0.0000046 z=10 P=0.0000012 当q=0.3时 z=0 P=1.0000000 z=5 P=0.1773523 z=10 P=0.0416605 z=15 P=0.0101008 z=20 P=0.0024804 z=25 P=0.0006132 z=30 P=0.0001522 z=35 P=0.0000379 z=40 P=0.0000095 z=45 P=0.0000024 z=50 P=0.0000006 求解令P<0.1%的z值: 为使P<0.001,则 q=0.10 z=5 q=0.15 z=8 q=0.20 z=11 q=0.25 z=15 q=0.30 z=24 q=0.35 z=41 q=0.40 z=89 q=0.45 z=340 12.结论 我们在此提出了一种不需要信用中介的电子支付系统。我们首先讨论了通常的电子货币的电子签名原理,虽然这种系统为所有权提供了强有力的控制,但是不足以防止双重支付。为了解决这个问题,我们提出了一种采用工作量证明机制的点对点网络来记录交易的公开信息,只要诚实的节点能够控制绝大多数的CPU计算能力,就能使得攻击者事实上难以改变交易记录。该网络的强健之处在于它结构上的简洁性。节点之间的工作大部分是彼此独立的,只需要很少的协同。每个节点都不需要明确自己的身份,由于交易信息的流动路径并无任何要求,所以只需要尽其最大努力传播即可。节点可以随时离开网络,而想重新加入网络也非常容易,因为只需要补充接收离开期间的工作量证明链条即可。节点通过自己的CPU计算力进行投票,表决他们对有效区块的确认,他们不断延长有效的区块链来表达自己的确认,并拒绝在无效的区块之后延长区块以表示拒绝。 本框架包含了一个P2P电子货币系统所需要的全部规则和激励措施。 参考文献 [1] W Dai(戴伟),a scheme for a group of untraceable digital pseudonyms to pay each other with money and to enforce contracts amongst themselves without outside help(一种能够借助电子假名在群体内部相互支付并迫使个体遵守规则且不需要外界协助的电子现金机制), “B-money”, http://www.weidai.com/bmoney.txt , 1998 [2] H. Massias, X.S. Avila, and J.-J. Quisquater, "Design of a secure timestamping service with minimal trust requirements,"(在最小化信任的基础上设计一种时间戳服务器) In 20th Symposium on Information Theory in the Benelux, May 1999. [3] S. Haber, W.S. Stornetta, "How to time-stamp a digital document," (怎样为电子文件添加时间戳)In Journal of Cryptology, vol 3, No.2, pages 99-111, 1991. [4] D. Bayer, S. Haber, W.S. Stornetta, "Improving the efficiency and reliability of digital time-stamping,"(提升电子时间戳的效率和可靠性) In Sequences II: Methods in Communication, Security and Computer Science, pages 329-334, 1993. [5] S. Haber, W.S. Stornetta, "Secure names for bit-strings,"(比特字串的安全命名) In Proceedings of the 4th ACM Conference on Computer and Communications Security, pages 28-35, April 1997. on Computer and Communications Security, pages 28-35, April,1997. [6] A. Back, "Hashcash - a denial of service counter-measure,"(哈希现金——拒绝服务式攻击的克制方法) http://www.hashcash.org/papers/hashcash.pdf , 2002. [7] R.C. Merkle, "Protocols for public key cryptosystems," (公钥密码系统的协议)In Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, pages 122-133, April 1980. [8] W. Feller, "An introduction to probability theory and its applications," (概率学理论与应用导论)1957 原文 (附件:279424)




心血来潮,决定把上次搞得SVPWM算法的推导过程整理一下,然后.....今天整整一天都在干这个.............. 功夫不负有心人,终于搞出来个不像科普文章也不像论文的怪文........... 各位大神轻点拍砖啊........... 以下由bg8npk搬运 搞SVPWM的目的呢 ,其实就是想让电机平稳地旋转,这次主要说说三相同步电动机(PMSM)的SVPWM控制吧(三相电机结构还请各位自行google)。 PMSM定子的三个绕组嘛,其实可以看做三个电磁铁,而且它们的方向互相成120°角,而它们产生的磁场强度呢,又是和通过它们的电流成正比的,也就是说,我们可以分别控制三个绕组的电流,来分别控制三个电磁铁的磁场强度,进而在电动机内叠加出一个磁场矢量。 PMSM的转子我们可以看做一个简单的永磁体,而上面那个磁场矢量可以看做另一个磁铁,所以,三相同步电机的原理通俗的讲呢, 就是两个条形磁铁摞一块,转动上面那个,下面的也跟着转 ……… 而为了能让电机平稳地转动,我们希望电机的定子磁场矢量能以恒定的速度旋转,并且保持磁场强度大小不变。前面说到,磁场强度是和绕组电流成正比的,所以呢,我们需要一个在空间中匀速旋转并且大小不变的电流矢量(电流矢量是个比较抽象的概念,其实它是由三相绕组的三个电流叠加起来的,稍后会详解)。 假设某一时刻,这个电流矢量是这么个情况: 其中坐标系中α轴与电机的A相绕组的方向一致,β轴与α轴成90°角 那么经过一段很小的时间△t之后,这个电流矢量又旋转了一点点: 由向量的加法运算可知,为了让电流矢量实现这个旋转,我们需要在这个△t中给它施加一个△I: 如果我们忽略电动机绕组的内阻的话,电动机其实可以近似为一个感性的负载,那么对于一个电感,如果给它加上恒定的电压,他的电流会随着时间线性增长,所以,为了能给电动机施加这个△I,我们只需要用一个和△I方向相同的电压矢量,再作用△t的时间,就OK啦。 恩,接下来的一个△t也是差不多这么个情况,有点不同的是△I的方向也转了一点点。随着一个一个△t转下去,电流矢量会转上一整圈,然后△I也就会转上一整圈。当我们把△t取的足够小的时候,三角形I也就进化成为一个在空间中匀速旋转的矢量。 由此可见,我们只要给PMSM施加一个大小不变,匀速旋转的电压矢量,就可以在PMSM内部形成一个大小不变,匀速旋转的电流矢量,继而再形成一个大小不变,匀速旋转的磁场矢量*(相信很多人已经有点晕了……其实我也晕……..)* 接下来的问题就是………怎么通过控制三相绕组上的电压,才能叠加出我们想要的电压矢量呢? 首先呢,先来一张三相全桥的电路图: 恩,如果我们让一个桥臂输出高压,另两个桥臂输出0,那么我们可以得到三个互成120°角的电压矢量: 如果让两个桥臂输出高压,另一个输出0,那么我们又可以得到三个电压矢量,相当于两个基本电压矢量的叠加: 如果三个桥臂同时输出0或高压,PMSM的相电压为0,所以我们又得到两个零矢量。 可以看出,除了两个零向量外的六个矢量把整个平面分成了6个扇区,在任意时刻,旋转的电压矢量肯定会落在某一个扇区里。我们给这几个扇区编一下号: 其中U0 U2 U4这三个向量称为主向量,U1,U3,U5称为辅向量。易知U0-U5的幅度都相同,均为三相全桥的直流供电电压,设这个电压为Udc。 SVPWM的基本实现方法呢,**就是在一个PWM周期内。调节一个扇区两边的两个电压矢量和两个零矢量的作用时间,根据平均值等效原理,来合成出我们想要的电压矢量。**假设我们想要的电压矢量正好落在第一扇区里面,那么大致情况就是这样滴: 恩,设一个PWM周期为T,主向量U0作用时间为T1,辅向量U1作用时间为T2,零向量作用时间为T0,那么有T=T0+T1+T2 由向量的叠加与分解可知,我们要输出的Uout在两个坐标轴上的分量分别等于主向量辅向量经过平均值等效后的两个向量在两个坐标轴上的分量之和……好吧我承认我语文不好…….. 那么就有如下关系 Uout * COSθ =(T1/T)Udc+(T2/T)Udc * COS 60° Uout * SINθ =(T2/T)Udc * SIN 60° T0 = T-T1-T2 令Uα=Uout * COSθ Uβ=Uout * SINθ 解得 T0=(1-Uα/Udc -Uβ/√3*Udc)T T1=(Uα/Udc – Uβ/√3*Udc)T T2=2 Uβ T/√3*Udc 再令X=Uα T/Udc Y=Uβ T/√3*Udc 就有 T0=T-X-Y T1=X-Y T2=2*Y 设A B C三相全桥三个桥臂的占空比分别为D1 D2 D3,那么输出U1只需要A相桥臂输出高压其他两个桥臂输出0就行,U2需要A B两个桥臂输出高压,C相输出0,零向量可以让三个桥臂同时输出高压或0,一般情况下我们让两个零向量的作用时间相同,那么就有 D1=(T0/2 + T1 + T2)/T=(1+X+Y)/2 D2=(T0/2 + T2)/T=(1-X+3Y)/2 D3=T0/2*T=(1-X-Y)/2 恩,第一个扇区的推导就是这样,后面的五个扇区的推导过程类似,实在不想挨个把五个扇区的详细推导都打上来了…….太多了,接下来就把结果写上来吧 第二个扇区: D1=1/2 +X D2=1/2 +Y D3=1/2 –Y 第三个扇区 D1=(1+X-Y)/2 D2=(1-X+Y)/2 D3=(1-X-3*Y)/2 第四个扇区 D1=(1+X+Y)/2 D2= (1-X+3Y)/2 D3=(1-X-Y)/2 第五个扇区: D1=1/2 +X D2=1/2 +Y D3=1/2 –Y 第六个扇区 D1=(1+X-Y)/2 D2=(1-X+Y)/2 D3=(1-X-3*Y)/2 其实我们可以发现,第一第四扇区、第二第五扇区还有第三第六扇区的占空比都是相同的,有了这些占空比,我们就可以很方便的写出SVPWM程序了 最后附上C演示程序 #include <stdio.h> #include <math.h> #define PI 3.141592653 #define SQRT_3 1.732051 #define DEPTH 256 /*数据深度,即存储单元的个数*/ #define WIDTH 8 /*存储单元的宽度*/ void svpwm(void); unsigned int Sampling = 256; unsigned char CCR1, CCR2, CCR3; double x,y; double PI2; double cosa; double buffer[6]; double Vdc = 100; double Vo = SQRT_3*Vdc/2; int i; int s1,s2,s3,s4,s5; void init(void) { PI2 = PI * 2 / Sampling; s1 = Sampling/6; s2 = Sampling/3; s3 = Sampling/2; s4 = 2*Sampling/3; s5 = 5*Sampling/6; cosa = 2*cos(PI2); buffer[0] = -sin(PI2) *(Vo / Vdc) /(2* SQRT_3); buffer[1] = 0; buffer[2] = 0; buffer[3] = cos(PI2) * (Vo / Vdc)/2; buffer[4] = Vo / (Vdc*2); buffer[5] = Vo / (Vdc*2); } int main(void) { init( ); for (i = 0; i < Sampling; i++) { svpwm( ); printf("%d %d %d %d \n", i,CCR1,CCR2,CCR3); } } while(1); } void svpwm(void) { x = buffer[5]; y = buffer[2]; buffer[2] = cosa * buffer[1] - buffer[0]; buffer[5] = cosa * buffer[4] - buffer[3]; buffer[0] = buffer[1]; buffer[1] = buffer[2]; buffer[3] = buffer[4]; buffer[4] = buffer[5]; if (i <= s1) //判断扇区,计算占空比 { CCR1 = (0.5 + x + y)*256+256; CCR2 = (0.5 - x + 3*y)*256+256; CCR3 = (0.5 - x - y)*256+256; } else if (i <= s2) { CCR1 = (0.5 + 2*x)*256+256; CCR2 = (0.5 + 2*y)*256+256; CCR3 = (0.5 - 2*y)*256+256; } else if (i <= s3) { CCR1 = (0.5 + x - y)*256+256; CCR2 = (0.5 - x + y)*256+256; CCR3 = (0.5 - x - 3*y)*256+256; } else if (i <= s4) { CCR1 = (0.5 + x + y)*256+256; CCR2 = (0.5 - x + 3*y)*256+256; CCR3 = (0.5 - x - y)*256+256; } else if (i <= s5) { CCR1 = (0.5 + 2*x)*256+256; CCR2 = (0.5 + 2*y)*256+256; CCR3 = (0.5 - 2*y)*256+256; } else { CCR1 = (0.5 + x - y)*256+256; CCR2 = (0.5 - x + y)*256+256; CCR3 = (0.5 - x - 3*y)*256+256; } }


在我的上一篇文章末尾( http://bbs.kechuang.org/read/79505 ),我简要论述了“硬件安全模块”对互联网信息安全的重要性。如今,无数的银行业、支付业者使用HSM保证它们的业务不受黑客的破坏。 我提出的问题是:硬件安全模块能自己做吗? 在回答这个问题之前,不妨看看以下两篇文章。 http://netsecurity.51cto.com/art/201005/200658.htm http://www.safeploy.com/2014/12-4/20564851725.html 结合我的上一篇文章(如果没有读过,建议先读一下),一个最简单的实用HSM大概是这样工作的: 1)通过一定的接口与计算机通信 2)内部保存有私钥,且无法通过任何暴力手段获得该私钥 3)能够利用内部保存的私钥,签发证书,此证书可由对应的公钥验证。 其实,我们常说的“加密狗” “U盾”都属于HSM,但它们一般只能保存证书(而且不算特别安全),不一定具有签发证书的功能。 为此我画了一个草图。 是不是有点像树莓派?其实就是一个网络服务器,可以利用内置的OpenSSL软件签发证书,并返回给用户。 附加要求挺简单: 1)只能通过网络与外界联系,不允许通过其他方式访问。 网络接口对外必须不暴露任何漏洞,例如恶意流量导致的缓冲区溢出,等等。 2)盒子非常坚固,难以破坏、拆卸;即便拆开,也无法获取到内部存储的密钥。 3)无法从盒子之外通过各种窥探方式(例如电流监测、电磁辐射监测)有效地获知盒子内部的工作状态,也即无法窥探出密钥。 基于这样的HSM模块,我们可以涉足许多有意思的领域,例如谍报、保密,还有当下最火的支付业务。







Sorry! 抱歉 request to path /f/222?page=99999999999999999999 encountered an error. If you think this shouldn't happen, please contact the Site Admin. 请求的网址 /f/222?page=99999999999999999999 出错。如果您认为这个错误不应该发生,请通知网站管理员。 信息 message: ArangoError: LIMIT offset value is not a number or out of range (while instantiating plan)     at new ArangoError (F:\nkc\nkc2\node_modules\arangojs\lib\error.js:30:15)     at F:\nkc\nkc2\node_modules\arangojs\lib\connection.js:204:21     at callback (F:\nkc\nkc2\node_modules\arangojs\lib\util\request.node.js:52:9)     at IncomingMessage.<anonymous> (F:\nkc\nkc2\node_modules\arangojs\lib\util\request.node.js:63:11)     at emitNone (events.js:91:20)     at IncomingMessage.emit (events.js:185:7)     at endReadableNT (_stream_readable.js:974:12)     at _combinedTickCallback (internal/process/next_tick.js:80:11)     at process._tickCallback (internal/process/next_tick.js:104:9) nkc Development Server

Sorry! 抱歉 request to path /f/222?page=99999999999999999999 encountered an error. If you think this shouldn't happen, please contact the Site Admin. 请求的网址 /f/222?page=99999999999999999999 出错。如果您认为这个错误不应该发生,请通知网站管理员。 信息 message: ArangoError: LIMIT offset value is not a number or out of range (while instantiating plan)     at new ArangoError (F:\nkc\nkc2\node_modules\arangojs\lib\error.js:30:15)     at F:\nkc\nkc2\node_modules\arangojs\lib\connection.js:204:21     at callback (F:\nkc\nkc2\node_modules\arangojs\lib\util\request.node.js:52:9)     at IncomingMessage.<anonymous> (F:\nkc\nkc2\node_modules\arangojs\lib\util\request.node.js:63:11)     at emitNone (events.js:91:20)     at IncomingMessage.emit (events.js:185:7)     at endReadableNT (_stream_readable.js:974:12)     at _combinedTickCallback (internal/process/next_tick.js:80:11)     at process._tickCallback (internal/process/next_tick.js:104:9) nkc Development Server


首先介绍几个寄存器,这几个寄存器只能在汇编或内联汇编时才能访问,C语言是访问不到的 R0-R12,都可作为临时变量存储,跟C语言的变量差不多,不过汇编中的存储变量是用寄存器,而且不用声明,全局可见,不分全局和局部,而且是32位的 比如想计算1+1,结果放在r0中 mov r0,0x01 add r0,0x01 图片来自互联网,互联网来自<<Cortex-M3权威指南>> R13(MSP/PSP)堆栈寄存器,汇编指令PUSH,POP会影响R13的值, PUSH {R0} //R0的值将被压入栈内 R13-4 POP{R1}    //将SP所指向的内存赋值给R1 SP+4 R14 :是连接寄存器(LR),用于在调用子程序时存储返回地址,应该是不能用mov mrs msr访问的,只能用pop push保存 R15 是程序计数器(PC),每条指令前都对应一个地址,把这个地址赋值给R15,程序就立即跳过去执行地址对应的程序 xRPS特殊功能寄存器组 详细内容请参考<< Cortex-M3权威指南>> 之前用过UCOS,可是他每个死循环任务下面必须有一个延时函数,才可以把调度权限交还给系统,自然这个延时函数上面也不可以有死循环, 不然将永远卡在死循环上,别的任务也就不能再被调用了 int main(void) {           GPIO_InitTypeDef  GPIO_InitStructure;                          Stm32_Clock_Init(9); //系统时钟设置     delay_init(72);      //延时初始化     uart_init(72,9600);  //串口初始化为9600     LED_Init();          //初始化与LED连接的硬件接口       SysTick_Configuration();     OSInit();     OSTaskCreate( TaskStart,    //task pointer                     (void *)0,  //parameter                     (OS_STK *)&TASK_START_STK[START_STK_SIZE-1],    //task stack top pointer                     START_TASK_Prio );  //task priority                              OSStart();     return 0;       } //开始任务 void TaskStart(void * pdata) {     pdata = pdata;     OS_ENTER_CRITICAL();       OSTaskCreate(TaskLed, (void * )0, (OS_STK *)&TASK_LED_STK[LED_STK_SIZE-1], LED_TASK_Prio);     OSTaskCreate(TaskLed1, (void * )0, (OS_STK *)&TASK_LED1_STK[LED1_STK_SIZE-1], LED1_TASK_Prio);     OSTaskSuspend(START_TASK_Prio); //suspend but not delete     OS_EXIT_CRITICAL(); } //任务1 //控制DS0的亮灭. void TaskLed(void *pdata) {     while(1)     {         LED0 = !LED0;         OSTimeDlyHMSM(0,0,1,100);       } } //任务2 //控制DS1的亮灭. void TaskLed1(void *pdata) {     while(1)     {         LED1 = !LED1;         OSTimeDlyHMSM(0,0,1,300);       } } 正文开始: 我写的这个调度系统是用stm32 定时器中断调度任务的,任务中不需要ucos那种延时函数,定时器每隔一段时间中断当前任务,保存现场,恢复第二个任务的现场,这时PC寄存器被改为第二个任务上次中断前执行的位置然后退出中断,,从任务二上次中断前的位置继续执行 调度原理: 参考<<Cortex-M3权威指南>>中 第九章 中断的具体行为 当 C M 3 开始响应一个中断时内核会自动 把 8个寄存器(R0-R3,R12,LR,PC,xPSR)的值压入栈,最关键的是PC,他直接决定了中断退出以后开始执行的位置,R0-R3,R12则保存了一些中间变量,保证了恢复现场以后程序正确执行 程序流程:                                                                       标志:↓↓↓ 进入main() ----> 初始化GPIO,时钟,定时器,开中断 ---->进入任务0 ---->定时器中断时间到 ---->开始进入中断 ---->系统自动 把 8 个寄存器(R0-R3,R12,LR,PC,xPSR)的值压入栈 ---->进入中断函数TIM3_IRQHandler (此时SP堆栈指针正指向R0,R0+4后,指向R1) TIM3_IRQHandler PROC         PUSH     {r4,lr}         MRS      r4,MSP         MOV      r0,r4         ADD      r0,r0,#8         MOV      r4,r0         LDR      r0,|L0.640|         STR      r4,[r0,#0]         BL       IRQHandler         POP      {r4,pc}         ENDP   -----> 生成汇编文件后可以看到,进入 TIM3_IRQHandler函数先把R4和LR压栈(这是编译器自动做的),MSP堆栈指针 - 8   -----> MRS      r4,MSP 保存栈指针    ----->指针+8(对前面R4和LR的补偿)对准 8个被自动压栈的寄存器的R0    ----->保存指针到全局变量 Address_BASE    -----> bl    IRQHandler  调用任务调度程序    -----> 清除 中断待处理位    ----->根据之前保存的栈地址,加载8个寄存器保存当前任务现场    ----->    调用任务二的地址----->   task->Start_Next(); 更新任务标志----->    退出中断 并 恢复被修改了返回地址的8个寄存器 ----->    执行任务1 ----->    定时器中断时间到 ---->进入中断   -----> 保存任务1现场----->    恢复任务0  ----->退出中断   -----> goto 标志; #include "../h/main.h"                  extern Task *task; int main(void) {                      task = new Task(); //创建任务管理对象 这是一个C++类对象                          Init();//初始化 时钟 串口 GPIO     Timerx_Init(20,7199);//初始化TIM3,开中断,每2ms进一次中断     TIM3->CNT = 0x01;//意义不明 不要也行     __asm  //內联汇编     {         bl Task0 //跳转到任务1     } //  while (true); //  delete task; } int temp = 0; void Task0(void) //任务1 {     while (true)     {         LED0 = !LED0;     } }                  void Task1(void) //任务2 {     while (true)     {         LED1 = !LED1;     } } class Task //任务管理类 { public:Task(void) //构造函数,初始化时会自动调用     {         Reg_Buff[0][6]=((unsigned int)&Task0) ; //初始化任务0的指针         Reg_Buff[1][6]=((unsigned int)&Task1) ; //初始化任务1的指针         Reg_Buff[0][6]=Reg_Buff[1][7]=0x61000000 ; //初始化xRSP                          Current = 0;         Next = 1;     }     public: static const unsigned char Count = Task_Count;     public: unsigned char Current; //当前任务     public: unsigned char Next ;    //下一个任务     public: volatile unsigned int Reg_Buff[Task_Count][8];     public: void    Start_Next() //更新至下一个任务     {         (Current + 1 < Count) ? Current++ : Current = 0;         (Next + 1 < Count) ? Next++ : Next = 0;                  //      if (Next != 0 && (Next - Current) != 1) //      { //          while (true) //          {                  //          } //      }     }                  }; Task *task ; unsigned int Address_BASE = 0;                  void IRQHandler(void) {                          if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源     {         TIM_ClearITPendingBit(TIM3, TIM_IT_Update);  //清除TIMx的中断待处理位:TIM 中断源         __asm         {             ldr r5, [Address_BASE]               str r5, [&task->Reg_Buff[task->Current][0]]//R0                              ldr r5, [Address_BASE , 0x4]             str r5, [&task->Reg_Buff[task->Current][1]]//R1                              ldr r5, [Address_BASE , 0x8]             str r5, [&task->Reg_Buff[task->Current][2]]//R2                              ldr r5, [Address_BASE , 0xc]             str r5, [&task->Reg_Buff[task->Current][3]]//R3                              ldr r5, [Address_BASE , 0x10]             str r5, [&task->Reg_Buff[task->Current][4]]//R12                  //          ldr r5, [Address_BASE , 0x14] //          str r5, [&task->Reg_Buff[task->Current][5]]//R13 LR                              ldr r5, [Address_BASE , 0x18]                              str r5, [&task->Reg_Buff[task->Current][6]]//R14 PC                              ldr r5, [Address_BASE , 0x1c]             str r5, [&task->Reg_Buff[task->Current][7]]//R15 xRSP                                          /*↑↑↑保存当然运行中的任务现场↑↑↑*/                              ldr r5, [&task->Reg_Buff[task->Next][0]]//R0             str r5, [Address_BASE]                              ldr r5, [&task->Reg_Buff[task->Next][1]]//R1             str r5, [Address_BASE, 0x4]                              ldr r5, [&task->Reg_Buff[task->Next][2]]//R2             str r5, [Address_BASE, 0x8]                              ldr r5, [&task->Reg_Buff[task->Next][3]]//R3             str r5, [Address_BASE, 0xc]                              ldr r5, [&task->Reg_Buff[task->Next][4]]//R12             str r5, [Address_BASE, 0x10]                  //          ldr r5, [&task->Reg_Buff[task->Next][5]]//R13 LR //          str r5, [Address_BASE, 0x14]                              ldr r5, [&task->Reg_Buff[task->Next][6]]//R14 PC             //orr r5, 0x01             str r5, [Address_BASE, 0x18]                              ldr r5, [&task->Reg_Buff[task->Next][7]]//R15 xRSP             str r5, [Address_BASE, 0x1c]             /*↑↑↑恢复上一个任务的现场↑↑↑*/         }         task->Start_Next(); //下一个任务     } } extern "C" {     void TIM3_IRQHandler(void)   //TIM3中断  中断中不能有太多东西,否则进中断时压栈太多 MSP不容易计算     {         __ASM         {             mrs r4, msp             add r4, 0x08             str r4, [&Address_BASE]             bl    IRQHandler         }     }                  }


我记得世纪之初的时候,某本古老的书上有这么一句话,大概是这个意思,无论是float还是double,在CPU内部都是转换为80位浮点数运算的,因此float和double其实是一样快的。 但是时代变化太快,这句话现在还对不对呢?写了个程序验证一下。使用的是Visual C++ 2015 Update 2,编译为x64架构。为了避免调试器的干扰,直接使用Ctrl+F5运行。 程序如下: // realspeed.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <windows.h> #define veclen 1048576 float vec1[veclen]; float vec2[veclen]; float vec3[veclen]; double dvec1[veclen]; double dvec2[veclen]; double dvec3[veclen]; int main() { ULONGLONG tk1, tk2; for (int i = 0; i < veclen; i++) { vec1[i] = vec2[i] = 2.0f; vec3[i] = 0.0f; dvec1[i] = dvec2[i] = 2.0; dvec3[i] = 0.0; } printf("float:\n"); for (int i = 0; i < 10; i++) { QueryPerformanceCounter((LARGE_INTEGER*)&tk1); for (int i = 0; i < veclen; i++) { vec3[i] = vec1[i] * vec2[i]; } QueryPerformanceCounter((LARGE_INTEGER*)&tk2); printf("ticks: %lld\n", tk2 - tk1); } printf("double:\n"); for (int i = 0; i < 10; i++) { QueryPerformanceCounter((LARGE_INTEGER*)&tk1); for (int i = 0; i < veclen; i++) { dvec3[i] = dvec1[i] * dvec2[i]; } QueryPerformanceCounter((LARGE_INTEGER*)&tk2); printf("ticks: %lld\n", tk2 - tk1); } return 0; } Debug下Ctrl+F5直接运行: Release下Ctrl+F5直接运行: 可以看到,在Release编译下,float比double快得多,而在Debug编译下则几乎没有差别。这是为什么呢?在这里我们设置了个断点,进行一下反编译—— Debug下的反编译: // realspeed.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <windows.h> #define veclen 1048576 float vec1[veclen]; float vec2[veclen]; float vec3[veclen]; double dvec1[veclen]; double dvec2[veclen]; double dvec3[veclen]; int main() { 00007FF65F6717D0 push rbp 00007FF65F6717D2 push rdi 00007FF65F6717D3 sub rsp,1C8h 00007FF65F6717DA lea rbp,[rsp+20h] 00007FF65F6717DF mov rdi,rsp 00007FF65F6717E2 mov ecx,72h 00007FF65F6717E7 mov eax,0CCCCCCCCh // realspeed.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <windows.h> #define veclen 1048576 float vec1[veclen]; float vec2[veclen]; float vec3[veclen]; double dvec1[veclen]; double dvec2[veclen]; double dvec3[veclen]; int main() { 00007FF65F6717EC rep stos dword ptr [rdi] ULONGLONG tk1, tk2; for (int i = 0; i < veclen; i++) 00007FF65F6717EE mov dword ptr [rbp+44h],0 00007FF65F6717F5 jmp main+2Fh (07FF65F6717FFh) 00007FF65F6717F7 mov eax,dword ptr [rbp+44h] 00007FF65F6717FA inc eax 00007FF65F6717FC mov dword ptr [rbp+44h],eax 00007FF65F6717FF cmp dword ptr [rbp+44h],100000h 00007FF65F671806 jge main+0C7h (07FF65F671897h) { vec1[i] = vec2[i] = 2.0f; 00007FF65F67180C movsxd rax,dword ptr [rbp+44h] 00007FF65F671810 lea rcx,[vec2 (07FF65FA7C170h)] 00007FF65F671817 movss xmm0,dword ptr [__real@40000000 (07FF65F679D1Ch)] 00007FF65F67181F movss dword ptr [rcx+rax*4],xmm0 00007FF65F671824 movsxd rax,dword ptr [rbp+44h] 00007FF65F671828 lea rcx,[vec1 (07FF65F67C170h)] 00007FF65F67182F movss xmm0,dword ptr [__real@40000000 (07FF65F679D1Ch)] 00007FF65F671837 movss dword ptr [rcx+rax*4],xmm0 vec3[i] = 0.0f; 00007FF65F67183C movsxd rax,dword ptr [rbp+44h] 00007FF65F671840 lea rcx,[vec3 (07FF65FE7C170h)] 00007FF65F671847 xorps xmm0,xmm0 00007FF65F67184A movss dword ptr [rcx+rax*4],xmm0 dvec1[i] = dvec2[i] = 2.0; 00007FF65F67184F movsxd rax,dword ptr [rbp+44h] 00007FF65F671853 lea rcx,[dvec2 (07FF660A7C170h)] 00007FF65F67185A movsd xmm0,mmword ptr [__real@4000000000000000 (07FF65F679D20h)] 00007FF65F671862 movsd mmword ptr [rcx+rax*8],xmm0 00007FF65F671867 movsxd rax,dword ptr [rbp+44h] 00007FF65F67186B lea rcx,[dvec1 (07FF66027C170h)] 00007FF65F671872 movsd xmm0,mmword ptr [__real@4000000000000000 (07FF65F679D20h)] 00007FF65F67187A movsd mmword ptr [rcx+rax*8],xmm0 dvec3[i] = 0.0; 00007FF65F67187F movsxd rax,dword ptr [rbp+44h] 00007FF65F671883 lea rcx,[dvec3 (07FF66127C170h)] 00007FF65F67188A xorps xmm0,xmm0 00007FF65F67188D movsd mmword ptr [rcx+rax*8],xmm0 } 00007FF65F671892 jmp main+27h (07FF65F6717F7h) printf("float:\n"); 00007FF65F671897 lea rcx,[string "float:\n" (07FF65F679CF0h)] 00007FF65F67189E call printf (07FF65F6711CCh) for (int i = 0; i < 10; i++) 00007FF65F6718A3 mov dword ptr [rbp+64h],0 00007FF65F6718AA jmp main+0E4h (07FF65F6718B4h) 00007FF65F6718AC mov eax,dword ptr [rbp+64h] 00007FF65F6718AF inc eax 00007FF65F6718B1 mov dword ptr [rbp+64h],eax 00007FF65F6718B4 cmp dword ptr [rbp+64h],0Ah 00007FF65F6718B8 jge main+186h (07FF65F671956h) { QueryPerformanceCounter((LARGE_INTEGER*)&tk1); 00007FF65F6718BE lea rcx,[tk1] 00007FF65F6718C2 call qword ptr [__imp_QueryPerformanceCounter (07FF6621B3000h)] for (int i = 0; i < veclen; i++) 00007FF65F6718C8 mov dword ptr [rbp+84h],0 00007FF65F6718D2 jmp main+112h (07FF65F6718E2h) 00007FF65F6718D4 mov eax,dword ptr [rbp+84h] 00007FF65F6718DA inc eax 00007FF65F6718DC mov dword ptr [rbp+84h],eax 00007FF65F6718E2 cmp dword ptr [rbp+84h],100000h 00007FF65F6718EC jge main+15Ah (07FF65F67192Ah) { vec3[i] = vec1[i] * vec2[i]; 00007FF65F6718EE movsxd rax,dword ptr [rbp+84h] 00007FF65F6718F5 lea rcx,[vec1 (07FF65F67C170h)] 00007FF65F6718FC movsxd rdx,dword ptr [rbp+84h] 00007FF65F671903 lea r8,[vec2 (07FF65FA7C170h)] 00007FF65F67190A movss xmm0,dword ptr [rcx+rax*4] 00007FF65F67190F mulss xmm0,dword ptr [r8+rdx*4] 00007FF65F671915 movsxd rax,dword ptr [rbp+84h] 00007FF65F67191C lea rcx,[vec3 (07FF65FE7C170h)] 00007FF65F671923 movss dword ptr [rcx+rax*4],xmm0 } 00007FF65F671928 jmp main+104h (07FF65F6718D4h) QueryPerformanceCounter((LARGE_INTEGER*)&tk2); 00007FF65F67192A lea rcx,[tk2] 00007FF65F67192E call qword ptr [__imp_QueryPerformanceCounter (07FF6621B3000h)] printf("ticks: %lld\n", tk2 - tk1); 00007FF65F671934 mov rax,qword ptr [tk1] 00007FF65F671938 mov rcx,qword ptr [tk2] 00007FF65F67193C sub rcx,rax 00007FF65F67193F mov rax,rcx 00007FF65F671942 mov rdx,rax 00007FF65F671945 lea rcx,[string "ticks: %lld\n" (07FF65F679D00h)] 00007FF65F67194C call printf (07FF65F6711CCh) } 00007FF65F671951 jmp main+0DCh (07FF65F6718ACh) printf("double:\n"); 00007FF65F671956 lea rcx,[string "double:\n" (07FF65F679D10h)] 00007FF65F67195D call printf (07FF65F6711CCh) for (int i = 0; i < 10; i++) 00007FF65F671962 mov dword ptr [rbp+0A4h],0 00007FF65F67196C jmp main+1ACh (07FF65F67197Ch) 00007FF65F67196E mov eax,dword ptr [rbp+0A4h] 00007FF65F671974 inc eax 00007FF65F671976 mov dword ptr [rbp+0A4h],eax 00007FF65F67197C cmp dword ptr [rbp+0A4h],0Ah 00007FF65F671983 jge main+251h (07FF65F671A21h) { QueryPerformanceCounter((LARGE_INTEGER*)&tk1); 00007FF65F671989 lea rcx,[tk1] 00007FF65F67198D call qword ptr [__imp_QueryPerformanceCounter (07FF6621B3000h)] for (int i = 0; i < veclen; i++) 00007FF65F671993 mov dword ptr [rbp+0C4h],0 00007FF65F67199D jmp main+1DDh (07FF65F6719ADh) 00007FF65F67199F mov eax,dword ptr [rbp+0C4h] 00007FF65F6719A5 inc eax 00007FF65F6719A7 mov dword ptr [rbp+0C4h],eax 00007FF65F6719AD cmp dword ptr [rbp+0C4h],100000h 00007FF65F6719B7 jge main+225h (07FF65F6719F5h) { dvec3[i] = dvec1[i] * dvec2[i]; 00007FF65F6719B9 movsxd rax,dword ptr [rbp+0C4h] 00007FF65F6719C0 lea rcx,[dvec1 (07FF66027C170h)] 00007FF65F6719C7 movsxd rdx,dword ptr [rbp+0C4h] 00007FF65F6719CE lea r8,[dvec2 (07FF660A7C170h)] 00007FF65F6719D5 movsd xmm0,mmword ptr [rcx+rax*8] 00007FF65F6719DA mulsd xmm0,mmword ptr [r8+rdx*8] 00007FF65F6719E0 movsxd rax,dword ptr [rbp+0C4h] 00007FF65F6719E7 lea rcx,[dvec3 (07FF66127C170h)] 00007FF65F6719EE movsd mmword ptr [rcx+rax*8],xmm0 } 00007FF65F6719F3 jmp main+1CFh (07FF65F67199Fh) QueryPerformanceCounter((LARGE_INTEGER*)&tk2); 00007FF65F6719F5 lea rcx,[tk2] 00007FF65F6719F9 call qword ptr [__imp_QueryPerformanceCounter (07FF6621B3000h)] printf("ticks: %lld\n", tk2 - tk1); 00007FF65F6719FF mov rax,qword ptr [tk1] 00007FF65F671A03 mov rcx,qword ptr [tk2] 00007FF65F671A07 sub rcx,rax 00007FF65F671A0A mov rax,rcx 00007FF65F671A0D mov rdx,rax 00007FF65F671A10 lea rcx,[string "ticks: %lld\n" (07FF65F679D00h)] 00007FF65F671A17 call printf (07FF65F6711CCh) } 00007FF65F671A1C jmp main+19Eh (07FF65F67196Eh) return 0; 00007FF65F671A21 xor eax,eax } 00007FF65F671A23 mov edi,eax 00007FF65F671A25 lea rcx,[rbp-20h] 00007FF65F671A29 lea rdx,[__xt_z+220h (07FF65F679CC0h)] 00007FF65F671A30 call _RTC_CheckStackVars (07FF65F671136h) 00007FF65F671A35 mov eax,edi 00007FF65F671A37 lea rsp,[rbp+1A8h] 00007FF65F671A3E pop rdi 00007FF65F671A3F pop rbp 00007FF65F671A40 ret Release下的反编译: // realspeed.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <windows.h> #define veclen 1048576 float vec1[veclen]; float vec2[veclen]; float vec3[veclen]; double dvec1[veclen]; double dvec2[veclen]; double dvec3[veclen]; int main() { 00007FF6A9EF1070 mov qword ptr [rsp+18h],rbx 00007FF6A9EF1075 push rbp 00007FF6A9EF1076 push rsi 00007FF6A9EF1077 push rdi 00007FF6A9EF1078 push r12 00007FF6A9EF107A push r13 00007FF6A9EF107C push r14 00007FF6A9EF107E push r15 00007FF6A9EF1080 sub rsp,20h ULONGLONG tk1, tk2; for (int i = 0; i < veclen; i++) { vec1[i] = vec2[i] = 2.0f; 00007FF6A9EF1084 mov eax,40000000h 00007FF6A9EF1089 lea r12,[vec2 (07FF6A9EF3620h)] 00007FF6A9EF1090 mov rdi,r12 00007FF6A9EF1093 lea r13,[vec1 (07FF6AAAF3620h)] 00007FF6A9EF109A mov ecx,100000h vec3[i] = 0.0f; 00007FF6A9EF109F lea r15,[vec3 (07FF6AB6F3620h)] 00007FF6A9EF10A6 rep stos dword ptr [rdi] 00007FF6A9EF10A8 mov rdi,r13 dvec1[i] = dvec2[i] = 2.0; 00007FF6A9EF10AB lea r14,[dvec2 (07FF6AAEF3620h)] 00007FF6A9EF10B2 mov ecx,100000h 00007FF6A9EF10B7 lea rbp,[dvec1 (07FF6AA2F3620h)] 00007FF6A9EF10BE rep stos dword ptr [rdi] 00007FF6A9EF10C0 xor eax,eax dvec3[i] = 0.0; 00007FF6A9EF10C2 lea rsi,[dvec3 (07FF6ABAF3620h)] 00007FF6A9EF10C9 mov rdi,r15 00007FF6A9EF10CC mov ecx,100000h 00007FF6A9EF10D1 rep stos dword ptr [rdi] 00007FF6A9EF10D3 mov rax,4000000000000000h 00007FF6A9EF10DD mov rdi,r14 00007FF6A9EF10E0 mov ecx,100000h 00007FF6A9EF10E5 rep stos qword ptr [rdi] 00007FF6A9EF10E8 mov rdi,rbp 00007FF6A9EF10EB mov ecx,100000h 00007FF6A9EF10F0 rep stos qword ptr [rdi] 00007FF6A9EF10F3 xor eax,eax 00007FF6A9EF10F5 mov rdi,rsi 00007FF6A9EF10F8 mov ecx,100000h 00007FF6A9EF10FD rep stos qword ptr [rdi] } printf("float:\n"); 00007FF6A9EF1100 lea rcx,[string "float:\n" (07FF6A9EF2210h)] 00007FF6A9EF1107 call printf (07FF6A9EF1010h) 00007FF6A9EF110C mov ebx,0Ah 00007FF6A9EF1111 mov edi,ebx for (int i = 0; i < 10; i++) { QueryPerformanceCounter((LARGE_INTEGER*)&tk1); 00007FF6A9EF1113 lea rcx,[tk1] 00007FF6A9EF1118 call qword ptr [__imp_QueryPerformanceCounter (07FF6A9EF2000h)] for (int i = 0; i < veclen; i++) 00007FF6A9EF111E xor eax,eax 00007FF6A9EF1120 mov ecx,20000h 00007FF6A9EF1125 nop word ptr [rax+rax] { vec3[i] = vec1[i] * vec2[i]; 00007FF6A9EF1130 movups xmm0,xmmword ptr [rax+r13] 00007FF6A9EF1135 movups xmm1,xmmword ptr [rax+r12] 00007FF6A9EF113A lea rax,[rax+20h] 00007FF6A9EF113E mulps xmm1,xmm0 00007FF6A9EF1141 movups xmm0,xmmword ptr [rax+r13-10h] 00007FF6A9EF1147 movups xmmword ptr [rax+r15-20h],xmm1 00007FF6A9EF114D movups xmm1,xmmword ptr [rax+r12-10h] 00007FF6A9EF1153 mulps xmm1,xmm0 00007FF6A9EF1156 movups xmmword ptr [rax+r15-10h],xmm1 00007FF6A9EF115C sub rcx,1 00007FF6A9EF1160 jne main+0C0h (07FF6A9EF1130h) } QueryPerformanceCounter((LARGE_INTEGER*)&tk2); 00007FF6A9EF1162 lea rcx,[tk2] 00007FF6A9EF1167 call qword ptr [__imp_QueryPerformanceCounter (07FF6A9EF2000h)] printf("ticks: %lld\n", tk2 - tk1); 00007FF6A9EF116D mov rdx,qword ptr [tk2] printf("ticks: %lld\n", tk2 - tk1); 00007FF6A9EF1172 lea rcx,[string "ticks: %lld\n" (07FF6A9EF2218h)] 00007FF6A9EF1179 sub rdx,qword ptr [tk1] 00007FF6A9EF117E call printf (07FF6A9EF1010h) 00007FF6A9EF1183 sub rdi,1 00007FF6A9EF1187 jne main+0A3h (07FF6A9EF1113h) } printf("double:\n"); 00007FF6A9EF1189 lea rcx,[string "double:\n" (07FF6A9EF2228h)] 00007FF6A9EF1190 call printf (07FF6A9EF1010h) for (int i = 0; i < 10; i++) { QueryPerformanceCounter((LARGE_INTEGER*)&tk1); 00007FF6A9EF1195 lea rcx,[tk1] 00007FF6A9EF119A call qword ptr [__imp_QueryPerformanceCounter (07FF6A9EF2000h)] for (int i = 0; i < veclen; i++) 00007FF6A9EF11A0 xor eax,eax 00007FF6A9EF11A2 mov ecx,40000h 00007FF6A9EF11A7 nop word ptr [rax+rax] { dvec3[i] = dvec1[i] * dvec2[i]; 00007FF6A9EF11B0 movups xmm0,xmmword ptr [rax+rbp] 00007FF6A9EF11B4 movups xmm1,xmmword ptr [rax+r14] 00007FF6A9EF11B9 lea rax,[rax+20h] 00007FF6A9EF11BD mulpd xmm1,xmm0 00007FF6A9EF11C1 movups xmm0,xmmword ptr [rax+r14-10h] 00007FF6A9EF11C7 movups xmmword ptr [rax+rsi-20h],xmm1 00007FF6A9EF11CC movups xmm1,xmmword ptr [rax+rbp-10h] 00007FF6A9EF11D1 mulpd xmm1,xmm0 { dvec3[i] = dvec1[i] * dvec2[i]; 00007FF6A9EF11D5 movups xmmword ptr [rax+rsi-10h],xmm1 00007FF6A9EF11DA sub rcx,1 00007FF6A9EF11DE jne main+140h (07FF6A9EF11B0h) } QueryPerformanceCounter((LARGE_INTEGER*)&tk2); 00007FF6A9EF11E0 lea rcx,[tk2] 00007FF6A9EF11E5 call qword ptr [__imp_QueryPerformanceCounter (07FF6A9EF2000h)] printf("ticks: %lld\n", tk2 - tk1); 00007FF6A9EF11EB mov rdx,qword ptr [tk2] 00007FF6A9EF11F0 lea rcx,[string "ticks: %lld\n" (07FF6A9EF2218h)] 00007FF6A9EF11F7 sub rdx,qword ptr [tk1] 00007FF6A9EF11FC call printf (07FF6A9EF1010h) 00007FF6A9EF1201 sub rbx,1 00007FF6A9EF1205 jne main+125h (07FF6A9EF1195h) } return 0; 00007FF6A9EF1207 xor eax,eax } 00007FF6A9EF1209 mov rbx,qword ptr [rsp+70h] 00007FF6A9EF120E add rsp,20h 00007FF6A9EF1212 pop r15 00007FF6A9EF1214 pop r14 00007FF6A9EF1216 pop r13 00007FF6A9EF1218 pop r12 00007FF6A9EF121A pop rdi 00007FF6A9EF121B pop rsi 00007FF6A9EF121C pop rbp 00007FF6A9EF121D ret 可以看到,现在早已过了x87 FPU的年代,编译器并没有使用FPU指令,而是使用的SSE指令。 Debug编译下,为了调试方便,将每一个循环都完整表现出来了(循环计数为100000h,即1048576),并且使用了movss/mulss和movsd/mulsd这两组标量指令,速度当然差不多。 而Release编译下,则将循环计数精简为20000h(131072=1048576/8)和40000h(262144=1048576/4),并且使用了movups/mulps和movups/mulpd这两组矢量指令,每次循环内进行2次运算,总计进行40000h(262144=1048576/4)和80000h(524288=1048576/2)次运算。由于SSE寄存器是固定的128位宽,每次只能放置4个32位宽的float或2个64位宽的double数据,因此使用float的话,只需要进行1/4次运算,而使用double的话,则需要进行1/2次运算。 结论就是:对于标量运算,float和double没有显著差别,而对于矢量运算,float比double要快。 因此,在计算量庞大的图形运算中,通常使用float而不是double以提高运算速度。






类似于在数字签名中隐藏信息的”域下信道“技术 其实叫”隐写术“似乎才是更加合适的讲法…… 具体实现方法就是用同义词表将文中的词汇替换为多个同义词,以此来在自然语言的冗余中隐藏额外的信息。 比较重要的部分是分词和同义词词库,词库的丰富程度直接影响到文中能够用来替换的同义词数量,另外就是其中词义也要尽量相近,并且是常用的词汇。哈工大的”同义词词林“用起来效果并不好,虽然词汇量较为丰富,但是其中即使只保留了标注"="同义词的部分,仍有许多意思相差十分大的词,而且生辟的词也较多,会导致编码后文义完全不通顺,语言风格变化显著。之后我从某个用于”伪原创“的程序源码包中找到另一份同义词词库,虽然词汇量少了近一半,但其中词意思相差十分小,几乎都是常用词,效果显著。 因为没有做分词的原因,所以部分多字词会被拆分转换(例如”1般“),不过总体上数量不多。 同义词词典中有一些词出现在多个同一词表之中,而且转换后的顺序还与原来不同,更有转换后的词与原来的文段组合起来,被识别成另外的词的情况,产生了不少误码,因此使用了纠错编码。(7,4)汉明码在少数情况可用,大部分情况需要使用比较”暴力“的编码方式(三个bit表示一个bit这种...) 这种方法隐藏的信息只需要用同样的替换便可过滤掉(例如“伪原创”),健壮性低。其次有时容易出现一些正常文本比较少见的用词搭配/习惯。 示例: 原文在这里 78633 可以看出和原文差异不大。 人类之所以发展到现在的文明程度,如果要归结1些本质的东西,“创新”必定会名列前茅。的确,衣食住行用,没有几样一成不变的来自自然界,即使号称“纯天然”的农产品也很难例外。不管是所谓物质文明,仍是所谓精神文明,皆出自于历代先贤的立异。 人们对于立异产生系统的概念是近代以后的事情,晚近时期发展为“立异崇拜”,开始努力地探索立异的规律。立异到底有无规律呢?在不同的尺度上,人们找到一些零散的“规律”。但系统的看来,距离搞清楚还很远。甚至应该思考:如果立异有规律,还叫立异吗? 一、对于规律的自信 如果您驯养过宠物,也许有这样的经验:没有受过教育的猫,有时会跑到饭桌上偷吃东西。如果每次都给它1些惩罚,猫就会找到爬上桌子以及接下来的不良体验之间的“因果瓜葛”,某些聪慧的猫甚至晓得趁主人不在的时候上桌子。无疑,所有高等动物都拥有发现因果瓜葛,甚至想象以及求证某些复杂因素链的能力。 因果结合在1起,出自佛教用语“3世因果,循环不失”。在佛教传入中国之前,“因”就作为1个非常首要的词代表一种逻辑解释法了。人类文明对于因果的认识和寻求,正是来自生物本能趋利避害的自然演化。同语言和文字的发展相同步,当经验可以通过抽象的概念进行高密度和低失真的传承以后,对因果关系的好奇和追寻便进入了蓬勃发展时期,终究本能的发展到凡事都以因果释之。 因果瓜葛是人类最先有意识去认识的规律。紧随其后,人们发现有些事情没有确切的缘由——例如划拳的时候如果连输5轮,并不等于第六轮就能赢。真正理解“运气”是怎么回事,就成为人类文明的一个阶段性进步。在西方,一七世纪出生了概率论,“随机”的概念也很快深入人心。在东方,“或然”虽然在古代用来表示“也许”、“随意”等不肯定的意思,但直到近代似乎也没有普及的认识,各路诡辩家(思想家)都在试图用因果逻辑解释一切。从因果的必然,再到概率事件的或者然,是人类认知的第二类基本规律。 近代以来,对因果的认识获得了极大的发展。得益于笛卡尔时期开始的理论开辟,诞生了一整套反人道的法子,从而能够得到“可靠的”因果关系,而不是“由于吃乌骨鸡后感冒好了,所以乌骨鸡有医治感冒的作用”这类蒙昧的因果关系,极大的加快了文明的发展速度。因为掌握规律的巨大胜利,人们认为世间一切事物的规律都能够被揭露和利用,并将揭示规律作为人类最伟大的事业。历史上关于立异的阐述也大量的树立在对于规律的自信上,专司钻研发明创造的规律和方法的学科——创造学,就是这类自信的产物。 然而,跟着对于混沌和幂律的揭露,人们认识了一类新的“随机”——它们在必定尺度上的概率是没成心义的,只能“整体而言”,却不论如何也找不出规律,比如没法提早两3天预测地震具体哪一秒发生。尽管没有规律也是“规律”,但是新的认识第1次不留余地的挫伤了人类掌控世间1切的信心。屋漏偏逢连夜雨,在选定1些坐标对于专利进行计量钻研之后,很多人发现创新也具有幂律分布的特色。 二、创新构思 创新的产生必然需要一些高档的想象能力。认识因果关系首先需要想象两个或多个事物的相干性,然后想象其中一部份的充沛性或必要性,后者是1个思维检验的进程。从现象来看,人比动物更具有这样的想象能力,特别是人可以将事物抽象为概念再进行想象。如果“发现自己曾不知道的因果关系”属于创新,那么上述想象能力是可以发生立异的。事实上,揭示自然界的因果关系1直是科学创新的主要战场。 反过来看,当人们具备必定的知识以及经验之后,应用同样的想象能力,可以把已经掌握的规律迁移运用。历史上的确有人不少人,包括1些创造学家,认为1切发明创造都是对已有事物、已有规律加以综合以后,依照某些肯定的模式产生的,著名的TRIZ理论的基本意思就是如此。 但是上述正反两方面并不是立异的全体。正常的想象老是限定在已经有认识的规模内,对于世界上本没有的因或者本没有的果就无能为力。关于不确定性的认识,告诉人们有些事是偶然发生甚至不可预测的,但这些事必须是自然运行的产物,不论是地震、天气还是1台机器。总之,它必须基于已有思惟材料进行深度加工,不论这种加工精确、隐约或属于随机尝试,都不应无中生有。极可能,脱离现实进行想象,特别是抽象思惟及其虚构能力是人类的一种特有的本能。大到国家、民族,细节到天使头上的光环、语言文字符号,大部份想象中的共识最初都来源于这种虚构。 抽象和虚构是离散事件,但是在长时间的历史中看它则整体上是连续的。创新既源于抽象虚构,又被已有的思惟材料所局限。通俗的讲,不论诸葛亮多聪慧,也想不到原枪弹,即使他擅长无中生有。对于立异的这种连续性规律,也有人将其抽象为“技术进化论”。然而“进化论”的基础是随机变异,与创新其实不相同。 由于具有发达的想象能力和抽象虚构能力,人们可以在思想中进行假定、试错、求证、仿真等探索。对于具备科学理性素质的人来讲,严密的逻辑和稳定明确的概念体系(抽象思惟工具体系)是思想探索的有力武器。这些思想探索的满意输出就成为立异的种子,称为立异构思。 对于个人而言,立异构思就是立异,但是对人类世界则不一定,由于创新构思可能不靠谱或不新颖。如果不满足社会衡量1个东西是否属于立异的实时标准,这个所谓的立异则只能停留在“精神可嘉”的范畴。假如人类世界毫无遮拦的接受一切“立异”,想必也会把自己折腾到崩溃。 3、从创新构思到创新 在不同的场合,“创新”可以用作动词、名词、形容词和副词,包罗万象。从立异构思到产出被称为立异的事物,同样属于立异的一个阶段。对于不同的立异,这个阶段的情景大不相同,但拥有进程上的共性。通常而言,总是包括创新的固定、创新的分享和立异的认可等过程。对于科学钻研等工作,在立异的固定以前,还需要进行检修。 立异构思仅仅存在于人的思惟之中,离不开具体的人。将立异构思用脱离具体的人而存在的法子显现出来的进程叫做创新的固定。例如用文字书写下来,用话语描述和记录,制作成样品、产品等。创新的分享是指让更多的人了解创新,通常的办法包括发表论文、销售产品、申请专利(兼具固定作用),也包括告知朋友、发布在互联网上等办法。创新是客观的,但冠以创新之名却是1种文化现象。1项事物是不是属于创新,取决于它在社会中的生命力,这就是创新的认可。1项构思可能以产品方式固定,通过销售产品而分享,在市场竞争中获得了大量的用户,被后人称之为创新。 在为创新树碑立传的时候,人们经常疏忽保守以及竞争。一项构思最终成为立异,不仅要与其它构思竞争,同时还必需与保守竞争。保守以及竞争可以排除了掉大部份劣质的立异,有利于人类文化总体保持稳定健康。从这个角度来看,守旧其实不是创新的对峙面,而是立异的一部份。不过不是所有具有竞争优势的创新都一定带来好结果,特别是优势主要依托对人们直觉和情绪的调动的时候,倒退和折腾的风险就相对增加。 科学研究是以揭示规律为快感的一种流动。对于科学钻研的精神,人们曾经提出1个名言,叫做“大胆假定,小心求证”。“勇敢假设”是基于必定素材的想象以及虚构,“小心求证”在思惟阶段的主要内涵是结合已经知的理论与事实,通过逻辑思惟和思想实验来求证,它的满意输出即为创新构思。现代科学的结构极为复杂,以致于良多人钻研一辈子,仍然未能超越前人的知识范围。所以,其实不是所有创新构思都能转化为科学成果,必须突破前人的认识边界并且被充分验证,才有可能成为科学成果或叫学术创新。这是从事科学钻研需要的学历越来越高,需要的知识传布速度愈来愈快的1个缘由。 科学研究和技术发明都需要进行检验,这1进程通常早于创新的固定就已经开始,然后贯穿后续的分享以及认可过程。对于科学钻研来讲,理论检验是必须的,有相当一部份还要进行试验验证。科学家认为具有一定的可靠度以后再将其固定为论文,然后交由期刊进行同行评议,没有发现显明的缺陷,才进入发表或者分享阶段,尔后还会经历长时间的检验。如果1项成果从未被固定、分享、验证,实际上也无人认可,它就不成为社会心义上的创新。 4、创新的动力 为何人类酷爱创新?这可以从人的本能和社会文化两个角度来探索。就本能而言,目前主要有两类假定,其1是人类对创新体验有欣快感,比如立异能让人取得意外的紧张刺激感或者好奇知足感,如果能制造这种感受便能在进化中患上到优势。另一种假设认为立异来源于隐约处理,而隐约处理能够降低大脑的能耗,也能在进化中获得优势。就社会文化角度而言,则可以笼统的认为创新自古以来都是1件有逼格的事情,可以在熟人中炫耀,可以作为咭片结交目生人,可以取得金山银海,以及可以取悦妹子。以个人对于创新产生积极预期为基础,加上社会运转整体上赋予创新更多的回报,构成了创新的社会动力。 立异的产量基本服从于一些无比原始的基础条件,比如全社会主动寻求立异的协作人口总规模及其资源配置水平。在工业时代来临以前,立异不属于社会主流文化,规模很小同时也缺乏有效的资源配置。人们大规模有意识的主动追求立异,以至于把创新作为1个独立的工作,是从近代才开始的。 近代最重要的变化是:对规律的认知不再需要通过漫长的“生产生活实践”来积累。科学的诞生使患上认识自然规律与糊口出产实践完全分离(理论创新与实用创新分线发展,前者领先于后者),可靠的知识迅速增加,远远超过古代自然生息能到达的知识产出速度。这会带来什么后果呢? 在古代,铜和铁的发现纯属意外,又经过很长时间才患上以批量的出产——此后漫长的时间里,人们围绕这两种材料做出了数量巨大的创新,从兵器到日用器皿,凝结了所有能开动的智慧,工艺也达到了叹为观止的程度。科学出生之后,虽然科学理论的产生仍然需要借助运气,但科学理论大大超前社会经验,因此能够为后续的创新提供明确的方向。元素周期表很快被填满,不论是铁的冶炼仍是合金等新材料的开发都再也不是一件“纯属意外”的事情。 假设没有科学的古代,一个文明(譬如每一千万人规模)在无数的臆想、出产实践以及意外事件中脱颖一件较为首要的立异(至关于青铜的发明)的概率为千年1遇,那么沿着科学指明的方向并遵守科学方法,这个几率就会高达1年1遇。换句话说,不论对古代的个人或是整个国家而言,立异的预期患上利无比低,不会有人把立异作为独立追求,延续古法或稍有改进是最稳当的策略。而到了近代,这项工作的患上利已经足以创造所有时期的世界首富(不论是财富还是声誉)并成为民族繁荣富强的基础,立异就成为了一件人们必需主动追求的事情,从而形成了极大的规模以及对比有效的资源配置。此时,物质文明再也不是“副产品”,相反可以被真实地掌握。 现代的情况则对于上述过程有所逆转。1方面得益于工业和信息技术发展,工程技术(实用创新)效力很高,填(科学的)坑的速度加快,另外一方面科学在大部分领域的进展相对放缓,因此实用立异又进入了青铜器时代那种精雕细琢的境界。 综上所述,来自人类本能的立异动力不是那末容易扭转,但社会动力却在科学诞生和工业化以后迅速加强。维持自由市场经济,扩展立异协作规模以及投入自然科学钻研(包含相关人材培养)将继续成为未来推动创新高速发展的基本方法。 5、用创新发生立异 “用机器制造机器”是工业时代最清脆的口号。随后,“用机器发生立异”自然被提上日程,成为当今最紧迫、最前沿的课题。如果想让机器像人一样产生创新,就需要弄清楚人类立异的规律,至少搞清楚这项本能的发生机制。然而正如上文所见,想很快搞清楚不太现实,用机器来做出像人一样的立异,恐怕短期内也不会有可行的办法。 要解决这个问题,需要先提出一个问题:立异是否只有“人类创新”这1种,人类创新又是否只有上文描写的这一种?事实上人类立异的法子以及形式一直在变化,不仅有多种内涵,而且每种本身也在不断更新。机器创新其实不必须依照当今的人类立异来设定,完整可以有另外的情势和内容。宇宙中如果有其它智慧生物的话,很可能也有不同的“立异”,不是咱们可以理解的。 如果认为发现新的相关性以及因果性就属于立异的话,用机器相对容易实现。对未知领域,只要有足够多的基础数据,都有可能发现规律。比如我们需要使卫星在能耗和业务量之间优化配置,完全可以用机器来求得最好的管理策略并直接实行。如果由人来统计分析,可能创造出一套“×××节制法”的东西,还能发论文,在社会意义上已经属于立异。用机器也可以患上出这样的东西,只无非,创造这个机器(包含程序)被称为立异,而机器发生的效果往往就不被认为是机器发生的立异了。将来这种情况会越来越多,大规模的人口会去创造这样的机器,为什么不能认为这是在用创新产生创新呢? 机器也能够具有抽象能力,我们可让机器发生一些空概念,并且经由大量的数据分析为这些概念赋与某些特定的意义。本文对于人类创新的定义是建立在社会意义上的,它需要走完从构思到认可的全部过程。在这种语境下机器很难符合人类创新的定义,必然被嘲笑“有多少人工就有多少智能”。事实上对于机器而言,能抽象概念,能发现规律就可以视为创新了。将来还会有更多的机器被创造,而他们产出的不论概念还是规律,都不需要被人类理解。 其中隐藏的文字是: 我是测试文本,你们都看不见我 网页: https://gym487.github.io/hide/index.html 下载: https://github.com/gym487/hide/ 或 hide.zip 277k 12次



using System; using System.IO.Ports; using System.Text; using System.Threading; namespace IoRS485 { public class IOContrl { private SerialPort sP = new SerialPort(); public string[] SearchPort() { return SerialPort.GetPortNames(); } public int Open(string sPortName, string sBandRate) { int result; try { if (!this.sP.IsOpen) { this.sP.PortName = sPortName; this.sP.BaudRate = Convert.ToInt32(sBandRate); this.sP.WriteTimeout = 500; this.sP.ReadTimeout = 500; this.sP.Open(); } else { this.sP.Close(); this.sP.PortName = sPortName; this.sP.BaudRate = Convert.ToInt32(sBandRate); this.sP.Open(); } this.sP.ReadTimeout = 500; result = 0; } catch { result = -1; } return result; } public int Close() { int result; try { if (this.sP.IsOpen) { this.sP.Close(); } result = 0; } catch { result = -1; } return result; } public bool BufClear() { this.sP.DiscardInBuffer(); return true; } public string ReadStr(string cmd) { this.sP.DiscardInBuffer(); string text = "@" + cmd + "\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return text; } return "1"; } public string Reset(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Reset\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return text; } return "1"; } public string ReadSN(int addr) { string text = ""; try { this.sP.DiscardInBuffer(); text = "@" + addr.ToString() + ":*IDN?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); text = text.Replace("\r", ""); } catch { } if (text.Length > 0) { return text; } return "1"; } public int ReadAddr(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Addr?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); text = text.Replace("\r", ""); if (text.Length > 0) { return Convert.ToInt32(text); } return 1; } public string WriteAddr(int addr, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Addr ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetBTL(int addr, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":BRG ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string MotorRun(int addr, string motorNum, long steps) { if (steps == 0L) { return "0"; } this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Run:M", motorNum, " ", steps.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string MotorMoveTo(int addr, string motorNum, long steps) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:MoveTo:M", motorNum, " ", steps.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorPostion(int addr, string motorNum, long steps) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Pos:M", motorNum, " ", steps.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadMotorPostion(int addr, string motorNum) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Pos:M", motorNum, "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); string oldValue = "P" + motorNum + ":"; text = text.Replace(oldValue, ""); text = text.Replace("\n", ""); return text.Replace("\r", ""); } public string MotorHome(int addr, string motorNum) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Home:M", motorNum, "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorSpeed(int addr, string motorNum, int speed) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Speed:M", motorNum, " ", speed.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorInvert(int addr, string motorNum, int Invert) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Invert:M", motorNum, " ", Invert.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorSpeedStart(int addr, string motorNum, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:SStart:M", motorNum, " ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorSpeedAdd(int addr, string motorNum, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:SAdd:M", motorNum, " ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorSpeedHome(int addr, string motorNum, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:SHome:M", motorNum, " ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string MotorSave(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Motor:Save\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadMotorSpeed(int addr, string motorNum) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Speed:M", motorNum, "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); return text.Replace("\r", ""); } public string ReadMotoRunStatus(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Motor:Run?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("R:", ""); if (text.Length >= 4) { text = text.Substring(0, 4); } return text; } public string ReadMotoRunStatus(int addr, string motorNum) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Run:M", motorNum, "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); string oldValue = "R" + motorNum.Trim() + ":"; text = text.Replace(oldValue, ""); return text.Replace("\r\n", ""); } public string ReadMotoHomeStatus(int addr, string motorNum) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Home:M", motorNum, "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); return text.Replace("\r", ""); } public string MotoStop(int addr, string motorNum) { string text = "@" + addr.ToString() + ":Motor:Stop:All\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorMotorIntMode(int addr, int value) { if (value > 1 || value < 0) { value = 0; } this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:MINT ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string WriteOut(int addr, int channel, int onoff) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Output:Y", channel.ToString(), " ", onoff.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string WriteOutDelayOff(int addr, int channel, int delaytime) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":OutDly:Y", channel.ToString(), " ", delaytime.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string RealyOnOff(int addr, int value) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Output:Y9 "; text = text + value.ToString() + "\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadOutBit(int addr, int channel) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":OutPut:Y", channel.ToString(), "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); return text.Replace("\r", ""); } public string ReadIn(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Input:All?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); text = text.Replace("\r", ""); return text.Replace("X:", ""); } public string ReadInBit(int addr, int ch) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Input:X", ch.ToString(), "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); string oldValue = "X" + ch.ToString() + ":"; text = text.Replace(oldValue, ""); return text.Replace("\r\n", ""); } public int ReadInBit2(int addr, int port) { string text = this.ReadIn(addr); if (text.Length < 8) { return -1; } if (text[port - 1] == '1') { return 1; } return 0; } public string ReadCounter(int addr, int channel, out int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Count:C", channel.ToString(), "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); text = text.Replace("\r", ""); if (text.Length >= 1) { value = Convert.ToInt32(text); } else { value = 0; } return "1"; } public string XIntModeSet(int addr, int value) { if (value > 1 || value < 0) { value = 0; } this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:Int ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string XIntModeRead(int addr, int value) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Program:Int?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string MotorIntModeSet(int addr, int value) { if (value > 1 || value < 0) { value = 0; } this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:MInt ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadMotorIntMode(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Program:MInt?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetX8EStopMode(int addr, int value) { if (value > 1 || value < 0) { value = 0; } this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:EStop ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadX8EStopMode(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Motor:EStop?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetFilter(int addr, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Filter ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadAdc(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":ADC?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); return text.Replace("\r", ""); } public string EditWrite(int addr, int E_addr, int E_value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:Edit:", E_addr.ToString(), " ", E_value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string EditRead(int addr, int E_addr) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:Edit?:", E_addr.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadStep(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Program:Pstep?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return Convert.ToInt32(text).ToString(); } return "1"; } public int ReadPPStep(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Program:PPstep?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return Convert.ToInt32(text); } return -2; } public string SetOrigStep(int addr, int step) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:ORIG ", step.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return Convert.ToInt32(text).ToString(); } return "1"; } public string SetProgramMode(int addr, int mode) { string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:mode ", mode.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return text; } return "1"; } public string PtogramStop(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Program:Stop\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return text; } return "1"; } public string Pause(int addr, int value) { this.sP.DiscardInBuffer(); string text; if (value == 0) { text = "@" + addr.ToString() + ":Program:Pause 0\r\n"; } else { text = "@" + addr.ToString() + ":Program:Pause 1\r\n"; } this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return text; } return "1"; } public string InputPassWord(int addr, string password) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Compare:", password.Length.ToString(), ":", password, "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetPassWord(int addr, string password) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":LOCPASS:", password.Length.ToString(), ":", password, "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ClearPassWord(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Clear\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadCounterSpeed(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Count1?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadRotateDirection(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":FX?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string readPort() { string text = ""; int num = 0; string result; try { do { text += this.readPortOnce(); if (text.Substring(text.Length - 1, 1) == "\n") { break; } Thread.Sleep(5); num++; } while (num <= 20); result = text; } catch (Exception) { result = text; } return result; } public string readPortOnce() { string str = ""; byte[] array = new byte[this.sP.ReadBufferSize + 1]; int count = this.sP.Read(array, 0, this.sP.ReadBufferSize); string @string = Encoding.ASCII.GetString(array, 0, count); return str + @string; } public static int Asc(string character) { if (character.Length == 1) { ASCIIEncoding aSCIIEncoding = new ASCIIEncoding(); return (int)aSCIIEncoding.GetBytes(character)[0]; } throw new Exception("Character is not valid."); } } }

using System; using System.IO.Ports; using System.Text; using System.Threading; namespace IoRS485 { public class IOContrl { private SerialPort sP = new SerialPort(); public string[] SearchPort() { return SerialPort.GetPortNames(); } public int Open(string sPortName, string sBandRate) { int result; try { if (!this.sP.IsOpen) { this.sP.PortName = sPortName; this.sP.BaudRate = Convert.ToInt32(sBandRate); this.sP.WriteTimeout = 500; this.sP.ReadTimeout = 500; this.sP.Open(); } else { this.sP.Close(); this.sP.PortName = sPortName; this.sP.BaudRate = Convert.ToInt32(sBandRate); this.sP.Open(); } this.sP.ReadTimeout = 500; result = 0; } catch { result = -1; } return result; } public int Close() { int result; try { if (this.sP.IsOpen) { this.sP.Close(); } result = 0; } catch { result = -1; } return result; } public bool BufClear() { this.sP.DiscardInBuffer(); return true; } public string ReadStr(string cmd) { this.sP.DiscardInBuffer(); string text = "@" + cmd + "\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return text; } return "1"; } public string Reset(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Reset\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return text; } return "1"; } public string ReadSN(int addr) { string text = ""; try { this.sP.DiscardInBuffer(); text = "@" + addr.ToString() + ":*IDN?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); text = text.Replace("\r", ""); } catch { } if (text.Length > 0) { return text; } return "1"; } public int ReadAddr(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Addr?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); text = text.Replace("\r", ""); if (text.Length > 0) { return Convert.ToInt32(text); } return 1; } public string WriteAddr(int addr, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Addr ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetBTL(int addr, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":BRG ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string MotorRun(int addr, string motorNum, long steps) { if (steps == 0L) { return "0"; } this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Run:M", motorNum, " ", steps.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string MotorMoveTo(int addr, string motorNum, long steps) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:MoveTo:M", motorNum, " ", steps.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorPostion(int addr, string motorNum, long steps) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Pos:M", motorNum, " ", steps.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadMotorPostion(int addr, string motorNum) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Pos:M", motorNum, "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); string oldValue = "P" + motorNum + ":"; text = text.Replace(oldValue, ""); text = text.Replace("\n", ""); return text.Replace("\r", ""); } public string MotorHome(int addr, string motorNum) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Home:M", motorNum, "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorSpeed(int addr, string motorNum, int speed) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Speed:M", motorNum, " ", speed.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorInvert(int addr, string motorNum, int Invert) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Invert:M", motorNum, " ", Invert.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorSpeedStart(int addr, string motorNum, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:SStart:M", motorNum, " ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorSpeedAdd(int addr, string motorNum, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:SAdd:M", motorNum, " ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorSpeedHome(int addr, string motorNum, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:SHome:M", motorNum, " ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string MotorSave(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Motor:Save\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadMotorSpeed(int addr, string motorNum) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Speed:M", motorNum, "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); return text.Replace("\r", ""); } public string ReadMotoRunStatus(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Motor:Run?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("R:", ""); if (text.Length >= 4) { text = text.Substring(0, 4); } return text; } public string ReadMotoRunStatus(int addr, string motorNum) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Run:M", motorNum, "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); string oldValue = "R" + motorNum.Trim() + ":"; text = text.Replace(oldValue, ""); return text.Replace("\r\n", ""); } public string ReadMotoHomeStatus(int addr, string motorNum) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:Home:M", motorNum, "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); return text.Replace("\r", ""); } public string MotoStop(int addr, string motorNum) { string text = "@" + addr.ToString() + ":Motor:Stop:All\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetMotorMotorIntMode(int addr, int value) { if (value > 1 || value < 0) { value = 0; } this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:MINT ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string WriteOut(int addr, int channel, int onoff) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Output:Y", channel.ToString(), " ", onoff.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string WriteOutDelayOff(int addr, int channel, int delaytime) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":OutDly:Y", channel.ToString(), " ", delaytime.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string RealyOnOff(int addr, int value) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Output:Y9 "; text = text + value.ToString() + "\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadOutBit(int addr, int channel) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":OutPut:Y", channel.ToString(), "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); return text.Replace("\r", ""); } public string ReadIn(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Input:All?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); text = text.Replace("\r", ""); return text.Replace("X:", ""); } public string ReadInBit(int addr, int ch) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Input:X", ch.ToString(), "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); string oldValue = "X" + ch.ToString() + ":"; text = text.Replace(oldValue, ""); return text.Replace("\r\n", ""); } public int ReadInBit2(int addr, int port) { string text = this.ReadIn(addr); if (text.Length < 8) { return -1; } if (text[port - 1] == '1') { return 1; } return 0; } public string ReadCounter(int addr, int channel, out int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Count:C", channel.ToString(), "?\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); text = text.Replace("\r", ""); if (text.Length >= 1) { value = Convert.ToInt32(text); } else { value = 0; } return "1"; } public string XIntModeSet(int addr, int value) { if (value > 1 || value < 0) { value = 0; } this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:Int ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string XIntModeRead(int addr, int value) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Program:Int?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string MotorIntModeSet(int addr, int value) { if (value > 1 || value < 0) { value = 0; } this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:MInt ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadMotorIntMode(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Program:MInt?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetX8EStopMode(int addr, int value) { if (value > 1 || value < 0) { value = 0; } this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Motor:EStop ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadX8EStopMode(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Motor:EStop?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetFilter(int addr, int value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Filter ", value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadAdc(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":ADC?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); text = text.Replace("\n", ""); return text.Replace("\r", ""); } public string EditWrite(int addr, int E_addr, int E_value) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:Edit:", E_addr.ToString(), " ", E_value.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string EditRead(int addr, int E_addr) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:Edit?:", E_addr.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadStep(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Program:Pstep?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return Convert.ToInt32(text).ToString(); } return "1"; } public int ReadPPStep(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Program:PPstep?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return Convert.ToInt32(text); } return -2; } public string SetOrigStep(int addr, int step) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:ORIG ", step.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return Convert.ToInt32(text).ToString(); } return "1"; } public string SetProgramMode(int addr, int mode) { string text = string.Concat(new string[] { "@", addr.ToString(), ":Program:mode ", mode.ToString(), "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return text; } return "1"; } public string PtogramStop(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Program:Stop\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return text; } return "1"; } public string Pause(int addr, int value) { this.sP.DiscardInBuffer(); string text; if (value == 0) { text = "@" + addr.ToString() + ":Program:Pause 0\r\n"; } else { text = "@" + addr.ToString() + ":Program:Pause 1\r\n"; } this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); if (text.Length > 0) { return text; } return "1"; } public string InputPassWord(int addr, string password) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":Compare:", password.Length.ToString(), ":", password, "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string SetPassWord(int addr, string password) { this.sP.DiscardInBuffer(); string text = string.Concat(new string[] { "@", addr.ToString(), ":LOCPASS:", password.Length.ToString(), ":", password, "\r\n" }); this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ClearPassWord(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Clear\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadCounterSpeed(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":Count1?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string ReadRotateDirection(int addr) { this.sP.DiscardInBuffer(); string text = "@" + addr.ToString() + ":FX?\r\n"; this.sP.Write(text.ToCharArray(), 0, text.Length); text = this.readPort(); return text.Replace("\r\n", ""); } public string readPort() { string text = ""; int num = 0; string result; try { do { text += this.readPortOnce(); if (text.Substring(text.Length - 1, 1) == "\n") { break; } Thread.Sleep(5); num++; } while (num <= 20); result = text; } catch (Exception) { result = text; } return result; } public string readPortOnce() { string str = ""; byte[] array = new byte[this.sP.ReadBufferSize + 1]; int count = this.sP.Read(array, 0, this.sP.ReadBufferSize); string @string = Encoding.ASCII.GetString(array, 0, count); return str + @string; } public static int Asc(string character) { if (character.Length == 1) { ASCIIEncoding aSCIIEncoding = new ASCIIEncoding(); return (int)aSCIIEncoding.GetBytes(character)[0]; } throw new Exception("Character is not valid."); } } }


C++字符有四个类型,char是1字节,wchar_t可能是2字节或4字节,char16_t和char32_t顾名思义。char和wchar_t是C++98就有的,支持也最好。char16_t和char32_t是C++11加入的,直到现在也没有很好的支持。 由于emoji这种东西的出现,支持Windows-1252/GBK/Big5已经不够用了,要支持Unicode。但是Windows中char编码是定死的,不支持Unicode。要想支持Unicode,不是说其它方法不行,而是使用wchar_t作为字符类型是最方便的做法。 iostream宽字符流对象,虽然在Windows下不支持Unicode,但是它们是写作wchar_t程序的必备武器,这是因为字符输入输出是最简单常用的程序交互形式,它们可进行编码自动转换,解决了wchar_t的交互问题,并且所有平台都有提供,不需要学习平台相关的API。 但是iostream库在Linux下有一个坑,默认情况下wcout和cout不能混用。这是因为受到Linux下的C语言stdio.h库的掣肘: Windows下printf和wprintf可以混用,fwide函数是空函数。 Linux下printf和wprintf不可以混用,流的宽窄取决于首次调用哪个函数或先调用fwide(stream, 1)还是fwide(stream, -1),一旦确定即不可更改,除非重新打开。 但是另一方面,Visual C++直到2015才支持重新打开相同文件的操作,之前一直不行,并且重新打开相同文件也是非常低效的做法。 不过实际上,只要提前调用了 ios::sync_with_stdio(false) ,wcout和cout便不会通过stdio.h进行输入输出,而是自己管理缓冲区,就避免了wcout和cout不能混用的问题。 #include <iostream> #include <locale> using namespace std; int main() { // 不使用stdio.h进行输入输出,避免Linux/macOS下libc的宽窄流限制 ios::sync_with_stdio(false); // 设置locale locale::global(locale("")); cout.imbue(locale()); wcout.imbue(locale()); // 现在可以混用cout和wcout了 cout << "Hello, 窄 world!" << endl; wcout << L"Hello, 宽 world!" << endl; return 0; } 如果只更改编码,不更改区域格式的话: locale::global(locale(locale("C"), new codecvt_byname<wchar_t, char, mbstate_t>(""))); 实际测试中,Ubuntu 16.04下的GCC和Windows下的Visual C++ 2017、Visual C++ 2008、Mac OS X 10.11下的Clang均通过了测试,正确输出了两个字符串。 一些非常老的Mac OS X可能会报locale("")错误,但是现在所有新版Mac OS X或macOS都是用的Clang编译器,已完全不存在这个问题。 Visual C++ 2005比较特殊。这个版本的iostream本身就有bug,只要设置locale以后cout和wcout就都没办法输出中文字符串了,不过只对控制台是有bug的,对文件没有。因此如果要兼容这个版本的话,最好配合ReadConsole/WriteConsole使用。不过,大部分情况下,我们都可以改用Visual C++ 2008来解决这个问题。 Ubuntu 16.04下的GCC输出结果(Mac OS X 10.11下的Clang差不多,从略): pc@DESKTOP-PNUCILD:~$ g++ abc.cpp pc@DESKTOP-PNUCILD:~$ ./a.out Hello, 窄 world! Hello, 宽 world! pc@DESKTOP-PNUCILD:~$ Windows 10 Build 15063下Visual C++ 2017输出结果: D:\>cl /EHsc abc.cpp Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25019 for x86 Copyright (C) Microsoft Corporation. All rights reserved. abc.cpp Microsoft (R) Incremental Linker Version 14.10.25019.0 Copyright (C) Microsoft Corporation. All rights reserved. /out:abc.exe abc.obj D:\>abc Hello, 窄 world! Hello, 宽 world! D:\> Windows 10 Build 15063下Visual C++ 2008输出结果: D:\>cl /EHsc abc.cpp Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. abc.cpp Microsoft (R) Incremental Linker Version 9.00.21022.08 Copyright (C) Microsoft Corporation. All rights reserved. /out:abc.exe abc.obj D:\>abc Hello, 窄 world! Hello, 宽 world! D:\> Windows 10 Build 15063下Visual C++ 2005输出结果: D:\>cl /EHsc abc.cpp 用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 14.00.50727.42 版 版权所有(C) Microsoft Corporation。保留所有权利。 abc.cpp Microsoft (R) Incremental Linker Version 8.00.50727.42 Copyright (C) Microsoft Corporation. All rights reserved. /out:abc.exe abc.obj D:\>abc Hello, Hello, D:\>abc > abc.txt D:\>type abc.txt Hello, 窄 world! Hello, 宽 world! D:\>

C++字符有四个类型,char是1字节,wchar_t可能是2字节或4字节,char16_t和char32_t顾名思义。char和wchar_t是C++98就有的,支持也最好。char16_t和char32_t是C++11加入的,直到现在也没有很好的支持。 由于emoji这种东西的出现,支持Windows-1252/GBK/Big5已经不够用了,要支持Unicode。但是Windows中char编码是定死的,不支持Unicode。要想支持Unicode,不是说其它方法不行,而是使用wchar_t作为字符类型是最方便的做法。 iostream宽字符流对象,虽然在Windows下不支持Unicode,但是它们是写作wchar_t程序的必备武器,这是因为字符输入输出是最简单常用的程序交互形式,它们可进行编码自动转换,解决了wchar_t的交互问题,并且所有平台都有提供,不需要学习平台相关的API。 但是iostream库在Linux下有一个坑,默认情况下wcout和cout不能混用。这是因为受到Linux下的C语言stdio.h库的掣肘: Windows下printf和wprintf可以混用,fwide函数是空函数。 Linux下printf和wprintf不可以混用,流的宽窄取决于首次调用哪个函数或先调用fwide(stream, 1)还是fwide(stream, -1),一旦确定即不可更改,除非重新打开。 但是另一方面,Visual C++直到2015才支持重新打开相同文件的操作,之前一直不行,并且重新打开相同文件也是非常低效的做法。 不过实际上,只要提前调用了 ios::sync_with_stdio(false) ,wcout和cout便不会通过stdio.h进行输入输出,而是自己管理缓冲区,就避免了wcout和cout不能混用的问题。 #include <iostream> #include <locale> using namespace std; int main() { // 不使用stdio.h进行输入输出,避免Linux/macOS下libc的宽窄流限制 ios::sync_with_stdio(false); // 设置locale locale::global(locale("")); cout.imbue(locale()); wcout.imbue(locale()); // 现在可以混用cout和wcout了 cout << "Hello, 窄 world!" << endl; wcout << L"Hello, 宽 world!" << endl; return 0; } 如果只更改编码,不更改区域格式的话: locale::global(locale(locale("C"), new codecvt_byname<wchar_t, char, mbstate_t>(""))); 实际测试中,Ubuntu 16.04下的GCC和Windows下的Visual C++ 2017、Visual C++ 2008、Mac OS X 10.11下的Clang均通过了测试,正确输出了两个字符串。 一些非常老的Mac OS X可能会报locale("")错误,但是现在所有新版Mac OS X或macOS都是用的Clang编译器,已完全不存在这个问题。 Visual C++ 2005比较特殊。这个版本的iostream本身就有bug,只要设置locale以后cout和wcout就都没办法输出中文字符串了,不过只对控制台是有bug的,对文件没有。因此如果要兼容这个版本的话,最好配合ReadConsole/WriteConsole使用。不过,大部分情况下,我们都可以改用Visual C++ 2008来解决这个问题。 Ubuntu 16.04下的GCC输出结果(Mac OS X 10.11下的Clang差不多,从略): pc@DESKTOP-PNUCILD:~$ g++ abc.cpp pc@DESKTOP-PNUCILD:~$ ./a.out Hello, 窄 world! Hello, 宽 world! pc@DESKTOP-PNUCILD:~$ Windows 10 Build 15063下Visual C++ 2017输出结果: D:\>cl /EHsc abc.cpp Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25019 for x86 Copyright (C) Microsoft Corporation. All rights reserved. abc.cpp Microsoft (R) Incremental Linker Version 14.10.25019.0 Copyright (C) Microsoft Corporation. All rights reserved. /out:abc.exe abc.obj D:\>abc Hello, 窄 world! Hello, 宽 world! D:\> Windows 10 Build 15063下Visual C++ 2008输出结果: D:\>cl /EHsc abc.cpp Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. abc.cpp Microsoft (R) Incremental Linker Version 9.00.21022.08 Copyright (C) Microsoft Corporation. All rights reserved. /out:abc.exe abc.obj D:\>abc Hello, 窄 world! Hello, 宽 world! D:\> Windows 10 Build 15063下Visual C++ 2005输出结果: D:\>cl /EHsc abc.cpp 用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 14.00.50727.42 版 版权所有(C) Microsoft Corporation。保留所有权利。 abc.cpp Microsoft (R) Incremental Linker Version 8.00.50727.42 Copyright (C) Microsoft Corporation. All rights reserved. /out:abc.exe abc.obj D:\>abc Hello, Hello, D:\>abc > abc.txt D:\>type abc.txt Hello, 窄 world! Hello, 宽 world! D:\>


这两个字在Windows操作系统下都是chun鱼的chun,但是前者在其它系统下却可能变为乱码。 这是由于前者是GBK增补字符集PUA编码,而后者是Unicode标准非PUA编码。 受影响的有106个字符。由于不是所有字体都支持这106个PUA字符,导致了UTF-8在这106个字符上实际上并不跨平台。考虑到大多数操作系统都支持Unicode,如果要让文本跨平台使用,最好转换为非PUA编码。 转换时要注意,有6个字符需要映射到扩展平面Ext-B区域,需要两个UTF-16字符表示。 在其它操作系统下乱码的原因,主要是PUA区域除了GBK以外,也被Big5-HKSCS使用,操作系统可能会将这些字符显示为Big5-HKSCS字符。 判断这些字的方法也很简单,如果打出来的字符只有宋体没有雅黑,说明是PUA字符,建议用更通用的字符代替。 PUA范围 对应的PUA字符 E76C-E76C  E78D-E796  E7C7-E7C8  E7E7-E7F3  E815-E815  E819-E830  E832-E83A  E83C-E854  E856-E864  PUA范围 对应的非PUA字符 E76C-E76C € E78D-E796 ︐︒︑︓︔︕︖︗︘︙ E7C7-E7C8 ḿǹ E7E7-E7F3 〾⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻ E815-E815 ⺁ E819-E830 ⺄㑳㑇⺈⺋龴㖞㘚㘎⺌⺗㥮㤘龵㧏㧟㩳㧐龶龷㭎㱮㳠⺧ E832-E83A 龸⺪䁖䅟⺮䌷⺳⺶⺷ E83C-E854 䎱䎬⺻䏝䓖䙡䙌龹䜣䜩䝼䞍⻊䥇䥺䥽䦂䦃䦅䦆䦟䦛䦷䦶龺 E856-E864 䲣䲟䲠䲡䱷䲢䴓䴔䴕䴖䴗䴘䴙䶮龻 扩展平面(UTF-16需保存为两个字符): E816 20087[𠂇] E817 20089[𠂉] E818 200CC[𠃌] E831 215D7[𡗗] E83B 2298F[𢦏] E855 241FE[𤇾] puamap_txt_pic.rar 48.4k

这两个字在Windows操作系统下都是chun鱼的chun,但是前者在其它系统下却可能变为乱码。 这是由于前者是GBK增补字符集PUA编码,而后者是Unicode标准非PUA编码。 受影响的有106个字符。由于不是所有字体都支持这106个PUA字符,导致了UTF-8在这106个字符上实际上并不跨平台。考虑到大多数操作系统都支持Unicode,如果要让文本跨平台使用,最好转换为非PUA编码。 转换时要注意,有6个字符需要映射到扩展平面Ext-B区域,需要两个UTF-16字符表示。 在其它操作系统下乱码的原因,主要是PUA区域除了GBK以外,也被Big5-HKSCS使用,操作系统可能会将这些字符显示为Big5-HKSCS字符。 判断这些字的方法也很简单,如果打出来的字符只有宋体没有雅黑,说明是PUA字符,建议用更通用的字符代替。 (附件:274663) PUA范围 对应的PUA字符 E76C-E76C  E78D-E796  E7C7-E7C8  E7E7-E7F3  E815-E815  E819-E830  E832-E83A  E83C-E854  E856-E864  PUA范围 对应的非PUA字符 E76C-E76C € E78D-E796 ︐︒︑︓︔︕︖︗︘︙ E7C7-E7C8 ḿǹ E7E7-E7F3 〾⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻ E815-E815 ⺁ E819-E830 ⺄㑳㑇⺈⺋龴㖞㘚㘎⺌⺗㥮㤘龵㧏㧟㩳㧐龶龷㭎㱮㳠⺧ E832-E83A 龸⺪䁖䅟⺮䌷⺳⺶⺷ E83C-E854 䎱䎬⺻䏝䓖䙡䙌龹䜣䜩䝼䞍⻊䥇䥺䥽䦂䦃䦅䦆䦟䦛䦷䦶龺 E856-E864 䲣䲟䲠䲡䱷䲢䴓䴔䴕䴖䴗䴘䴙䶮龻 扩展平面(UTF-16需保存为两个字符): E816 20087[𠂇] E817 20089[𠂉] E818 200CC[𠃌] E831 215D7[𡗗] E83B 2298F[𢦏] E855 241FE[𤇾] (附件:274679)



字符串转换成数字 首先,不建议使用sscanf和atoi、atol、atoll、atof,它们无法检测溢出,而且atoX系列无法得知转换在何时结束。 C89函数,字节字符串: strtol - 32位(windows)/64位(linux)有符号整数long,可指定进制 strtoul - 32位(windows)/64位(linux)无符号整数unsigned long,可指定进制 strtod - 双精度浮点数double C95函数,宽字符串: wcstol - 32位(windows)/64位(linux)有符号整数long,可指定进制 wcstoul - 32位(windows)/64位(linux)无符号整数unsigned long,可指定进制 wcstod - 双精度浮点数double C99函数,字节字符串(需要VC2013): strtoll - 64位有符号整数long long,可指定进制 strtoull - 64位无符号整数unsigned long long,可指定进制 strtoimax - 64位有符号整数intmax_t,可指定进制 strtoumax - 64位无符号整数uintmax_t,可指定进制 strtof - 单精度浮点数float strtold - 扩展双精度浮点数long double C99函数,宽字符串(需要VC2013): wcstoll - 64位有符号整数long long,可指定进制 wcstoull - 64位无符号整数unsigned long long,可指定进制 wcstoimax - 64位有符号整数intmax_t,可指定进制 wcstoumax - 64位无符号整数uintmax_t,可指定进制 wcstof - 单精度浮点数float wcstold - 扩展双精度浮点数long double MSVC函数,字节字符串: _strtoi64 - 64位(windows)有符号整数__int64,可指定进制 _strtoui64 - 64位(windows)无符号整数unsigned __int64,可指定进制 MSVC函数,宽字符串: _wcstoi64 - 64位(windows)有符号整数__int64,可指定进制 _wcstoui64 - 64位(windows)无符号整数unsigned __int64,可指定进制 // long long and unsigned long long #define strtoll _strtoi64 #define strtoull _strtoui64 #define wcstoll _wcstoi64 #define wcstoull _wcstoui64 // intmax_t and uintmax_t #define strtoimax _strtoi64 #define strtoumax _strtoui64 #define wcstoimax _wcstoi64 #define wcstoumax _wcstoui64 // float static float strtof(char *pstr, char **ppend) { double dblval = strtod(pstr, ppend); if ((_finite(dblval) && !_finite((float)dblval)) || (dblval != 0 && (float)dblval == 0)) { errno = ERANGE; } return (float)dblval; } static float wcstof(wchar_t *pstr, wchar_t **ppend) { double dblval = wcstod(pstr, ppend); if ((_finite(dblval) && !_finite((float)dblval)) || (dblval != 0 && (float)dblval == 0)) { errno = ERANGE; } return (float)dblval; } // long double static long double strtold(char *pstr, char **ppend) { double dblval = strtod(pstr, ppend); return dblval; } static long double wcstold(wchar_t *pstr, wchar_t **ppend) { double dblval = wcstod(pstr, ppend); return dblval; } 数字转换成字符串 数字转换成字符串,一般使用sprintf系列。由于一些历史的原因,sprintf系列函数发生了比较复杂的演化。 不安全版本函数的支持情况: 原生支持C89标准的sprintf 原生支持C89标准的vsprintf 原生支持非标准的swprintf,VC2005后改为_swprintf 原生支持非标准的vswprintf,VC2005后改为_vswprintf 安全版本函数的支持情况: 原生支持非标准的_snprintf,见注释 原生支持非标准的_snwprintf,见注释 原生支持非标准的_vsnprintf,见注释(VC2015之前还被定义为vsnprintf) 原生支持非标准的_vsnwprintf,见注释 VC2005开始支持C95标准的swprintf(替换之前的不安全版本) VC2005开始支持C95标准的vswprintf(替换之前的不安全版本) VC2015开始支持C99标准的snprintf VC2015开始支持C99标准的vsnprintf(替换之前的非标准版本) 如果不需要安全性,可以直接使用sprintf和_swprintf。 如果需要兼容老版本VC,需要使用_snprintf和_snwprintf。 如果只需要兼容VC2005+,可以使用_snprintf和swprintf。 如果只需要兼容VC2015+,可以使用snprintf和swprintf。 注意_snprintf和_snwprintf和标准的版本不同: _snprintf和_snwprintf字符串不溢出时会用一个0截断,字符串溢出时不会截断。 VC2015: char buf[bufsz]; snprintf(buf, bufsz, "fmt", ...); 老版本VC实现方法: char buf[bufsz]; memset(buf, 0, bufsz); _snprintf(buf, bufsz - 1, "fmt", ...); 如果需要动态分配内存: #include <stdio.h> #include <string.h> #include <stdlib.h> int main() { char *buf = NULL; int reqsz = 0; reqsz = _snprintf(NULL, 0, "%d", 12); buf = malloc(reqsz + 1); memset(buf, 0, reqsz + 1); _snprintf(buf, reqsz, "%d", 12); printf("%s\n", buf); free(buf); return 0; }

如果使用不安全版本的sprintf和_swprintf,要注意估算所需缓冲区大小,最好略大于估算的大小,以免意外情况导致缓冲区不够用。 使用安全版本的snprintf和_snprintf、swprintf和_snwprintf不需要手动估算,可以先传递NULL,0计算一下生成的字符数量,使用malloc分配足够大的缓冲区,memset清零,再进行实际的格式化。 可以实现一系列fmtdup和wfmtdup函数,在堆中分配字符串并进行格式化: #include <stdio.h> #include <stdlib.h> #include <stdarg.h> // 字节字符串MSVC版 char *fmtdup(char *fmt, ...) { int reqsize = 0; char *buf = NULL; va_list va; va_start(va, fmt); reqsize = _vsnprintf(NULL, 0, fmt, va); va_end(va); va_start(va, fmt); if (reqsize != -1) if (buf = (char*)calloc(reqsize + 1, sizeof (char))) if (_vsnprintf(buf, reqsize, fmt, va) == -1) { free(buf); buf = NULL; } va_end(va); return buf; } // 宽字符MSVC版 wchar_t *wfmtdup(wchar_t *fmt, ...) { int reqsize = 0; wchar_t *buf = NULL; va_list va; va_start(va, fmt); reqsize = _vsnwprintf(NULL, 0, fmt, va); va_end(va); va_start(va, fmt); if (reqsize != -1) if (buf = (wchar_t*)calloc(reqsize + 1, sizeof(wchar_t))) if (_vsnwprintf(buf, reqsize, fmt, va) == -1) { free(buf); buf = NULL; } va_end(va); return buf; } #if !defined(_MSC_VER) || _MSC_VER >= 1900 // 字节字符C99/VS2015版 char *fmtdup2(char *fmt, ...) { // 注意:老版本VC也有vsnprintf,但是不符合标准 void *function_requires_c99_vs2015 = snprintf; int reqsize = 0; char *buf = NULL; va_list va; va_start(va, fmt); reqsize = vsnprintf(NULL, 0, fmt, va); va_end(va); va_start(va, fmt); if (reqsize != -1) if (buf = (char*)calloc(reqsize + 1, sizeof(char))) if (vsnprintf(buf, reqsize + 1, fmt, va) == -1) { free(buf); buf = NULL; } va_end(va); return buf; } #endif // _MSC_VER >= 1900 #if !defined(_MSC_VER) || _MSC_VER >= 1400 // 宽字符C95/VS2005版 wchar_t *wfmtdup2(wchar_t *fmt, ...) { int reqsize = 0; wchar_t *buf = NULL; va_list va; va_start(va, fmt); reqsize = vswprintf(NULL, 0, fmt, va); va_end(va); va_start(va, fmt); if (reqsize != -1) if (buf = (wchar_t*)calloc(reqsize + 1, sizeof(wchar_t))) if (vswprintf(buf, reqsize + 1, fmt, va) == -1) { free(buf); buf = NULL; } va_end(va); return buf; } #endif // _MSC_VER >= 1400




在Windows中C/C++默认使用ANSI字符串,ANSI字符串默认是一种窄字符串,可以用char保存,这对于学习C/C++语言的原理比较方便。但是Windows是一个基于Unicode的操作系统,在正式程序中使用ANSI字符串,会造成程序的不稳定因素,如本文附图所示。 WinAPI原生支持基于UTF-16的wchar_t宽字符,但是C/C++运行库默认并未被配置使用宽字符,想在C/C++运行库使用宽字符需要一些技巧。本文所述技巧并不使用任何WinAPI或ATL等外部库。这里主要讲C/C++中可以改成宽字符的三个地方,控制台函数、文本文件、命令行参数。 一、启用控制台函数的Unicode宽字符I/O模式(需使用wscanf、wprintf等宽字符函数) Windows的控制台支持两种I/O模式:当前代码页的窄字符I/O模式,以及UTF-16 LE宽字符I/O模式。 C/C++标准库默认的I/O模式是窄字符I/O模式,切换代码页会乱码。开启控制台UTF-16 LE宽字符I/O模式的方法是使用_setmode。将stdin/stdout/stderr的读写模式设为_O_U16TEXT,可以开启stdin/stdout/stderr的UTF-16 LE宽字符I/O模式,可以在不同代码页下正常工作。设为_O_TEXT可以恢复窄字符I/O模式。 一旦开启UTF-16 LE宽字符I/O模式,便无法再使用printf、scanf、cin、cout等窄字符I/O流(_getws_s和_putws等下划线函数也不支持),只能使用fgetws、fputws、wscanf、wprintf、wcin、wcout等宽字符I/O流。 #include <stdio.h> #include <io.h> // _setmode #include <fcntl.h> // _O_U16TEXT                                                                                        // C++包含文件 #include <iostream>                                                                                              int main() {     _setmode(_fileno(stdin), _O_U16TEXT); // 设置控制台为宽字符I/O模式     _setmode(_fileno(stdout), _O_U16TEXT);     _setmode(_fileno(stderr), _O_U16TEXT);                                                                                                  wchar_t buf2[200] = L"";     fgetws(buf2, 200, stdin);     wprintf(L"你输入了:%s", buf2); // 只能使用宽字符函数                                                                                              // C++     using namespace std;     wcout << L"C++宽字符测试" << endl;                                                                                                  return 0; } _setmode其它相关的取值: _O_U8TEXT 表示使用UTF-8编码的宽字符I/O模式,它在stdin上无法正常工作。 _O_WTEXT 表示通过检测BOM自动切换编码的宽字符I/O模式,它在stdin/stdout/stderr作用和_O_U16TEXT一样。 二、改变C标准库的默认区域映射 C标准库依赖区域映射来支持ANSI字符串和宽字符串的转换。不正确的区域设置会导致转换ANSI字符串时字符乱码或中断,因此如果程序使用了ANSI字符串,程序启动时必须要正确设置区域映射。 在C标准库中,程序启动时默认为“C”区域映射(ISO-8859-1),这个映射仅支持英语和西欧字符。要支持本地ANSI字符,需要程序启动时调用setlocale(LC_ALL, "");向setlocale传递一个空字符串,以读取系统默认的ANSI区域映射。 正确设置区域映射后,如果需要在wprintf、swprintf、fwprintf中使用ANSI字符串,可以使用L"%hs"限定符,函数会将它转换为宽字符。 #include <stdio.h> #include <io.h> // _setmode #include <fcntl.h> // _O_U16TEXT          int main() {     _setmode(_fileno(stdin), _O_U16TEXT); // 设置控制台为宽字符I/O模式     _setmode(_fileno(stdout), _O_U16TEXT);     _setmode(_fileno(stderr), _O_U16TEXT);                                                              wprintf(L"setlocale之前,窄字符测试:%hs\n", "我是ANSI窄字符"); // 乱码                                                              setlocale(LC_ALL, ""); // 读取系统区域设定                                                              wprintf(L"setlocale之后,窄字符测试:%hs\n", "我是ANSI窄字符"); // 正常                                                                  return 0; } 另外,正确设置区域映射后,虽然控制台已经可以输出宽字符中的中文,但是chcp切换代码页后也会乱码,遇到国际字符(如朝鲜文、越南文、泰文等)仍然会导致输出中断。这是因为这时控制台还是窄字符模式,只是C运行库的locale变为中文了而已。开启控制台Unicode宽字符输出的正确的方法是使用第一节所述的_setmode方法。 三、启用文本文件的Unicode(UTF-8/UTF-16)支持(需使用fgetws、fwprintf等宽字符函数) 读取文件时自动识别文件编码,写入时总是使用UTF-8: #include <stdio.h> #include <locale.h> // setlocale #include <io.h> // _setmode #include <fcntl.h> // _O_U16TEXT          int main() {     _setmode(_fileno(stdin), _O_U16TEXT); // 设置控制台为宽字符I/O模式     _setmode(_fileno(stdout), _O_U16TEXT);     _setmode(_fileno(stderr), _O_U16TEXT);              setlocale(LC_ALL, ""); // 读取系统区域设定,方便读取ANSI文档              wchar_t buf[200] = L"";     FILE *f = _wfopen(L"abc.txt", L"w+, ccs=UTF-8"); // 始终使用UTF-8保存(也可以使用UTF-16)     fwprintf(f, L"我是Unicode测试\n"); // 注意:使用宽字符函数     fclose(f);              f = _wfopen(L"abc.txt", L"r+, ccs=UNICODE"); // 通过BOM自动识别UTF-16、UTF-8、ANSI文档     fgetws(buf, 200, f);     fclose(f);              fputws(buf, stdout); // 显示读取的字符              return 0; } fopen和_wfopen的第二个参数可以附加一个可选项", ccs=<编码>",编码可以选择以下几种: ccs=UNICODE 通过检测BOM自动识别UTF-16、UTF-8、ANSI文本(对应_setmode的_O_WTEXT) ccs=UTF-16 始终按UTF-16打开和写入(对应_setmode的_O_U16TEXT) ccs=UTF-8 始终按UTF-8打开和写入(对应_setmode的_O_U8TEXT) 为了支持文件名中的国际字符,可以使用_wfopen函数。 C++的wfstream封装紧密,未找到原生支持上述特性的解决方案。 四、命令行参数使用宽字符 这个最简单,改用wmain作为入口点即可。代码如下: int wmain(int argc, wchar_t *argv[]) {     return 0; } Visual C++还支持第三个参数(环境变量),是可选的: int wmain(int argc, wchar_t *argv[], wchar_t *envp[]) {     return 0; } 五、使用ANSI字符串的安全性规则 由于ANSI字符串不能表示国际字符,因此在程序中最好不要使用ANSI字符串。必须使用ANSI字符串的话,要遵循这几个规则: 1. 允许将ANSI字符串转换为宽字符串(如使用L"%hs"限定符),允许读取ANSI文本文件。 2. 尽量避免将宽字符串转换为ANSI字符串(如使用"%ls"限定符),避免写入ANSI文本文件,以防信息丢失。 3. 尽量将ANSI字符串转换为宽字符串再处理,因为ANSI字符串变长且尾字节有冲突,不好处理。 将宽字符转换为ANSI字符串的正确方法是使用WideCharToMultiByte或使用ATL/MFC强制转换为(CStringA)或(CW2A),处理ANSI字符串的正确方法是使用_mbsstr或CStringA的成员函数,这超出了本文的范畴,不再讨论,有兴趣的话可以看我发的相关帖子或MSDN Library的相关章节。 附图:Windows的CHM查看器hh.exe使用ANSI字符串,无法处理国际字符。


现在我不太关心强AI还是弱AI,因为很多人实现AI的这条路本来就走错了。 人是动物,动物的本能是形象思维,抽象的概念系统是后天学习中建立的,是逻辑思维的基础。 真的要仿生,那么AI应该是建立在视频,听觉,触觉,图像,声音,刺激等综合信号与其产生的神经反应(包括冲动和记忆)构成的,我们中很多人走的路刚好相反,用的全是符号系统,形式逻辑,抽象的概念,本末倒置。结果就是——让机器理解“苹果”这这概念,需要走相当多的路,还不见得准确,因为它不会吃。AI和机器认知成了文字游戏,思维成了概念系统中的符号运算,可悲。 人的眼睛远比不上摄像头,因为人只关注视神经焦点上的东西,你看我打的字,同一时间只能注意其中1至3个字,屏幕上其他区域对你来说没有意义,你想看完整个屏幕,需要扫视,而不是注视。机器就不一样,它同一时间得到的是一整帧画面。让它一下子去处理这么大的信息量,实在难为电脑了。 我们走在屋子里,注意的只是眼前的东西,感觉的是身边一小片范围,而不是整个的桌子,椅子,只是能影响到你的那部分。我们会节约计算过程。我们看环境,是一小块一小块看的,让你看完风景马上闭上眼睛回忆,也想不出全部细节,因为我们只是在感觉。 机器视觉应该只处理黄斑上的东西,而不是整个画面。电脑应该理解3维物体而不是一个角度的映射。电脑的思维应该是形象的,三维也一样,需要扫视,而不是一下就都接收,虚拟3D空间的。 我们从任何角度看桌子都认识,电脑就不一定了。现在用弱AI也可以实现这个了。它们的模式识别是基于象素比较的,看人脸,看指纹,都是抽象化,取若干个点。 我关心的是实现,思想方面的有很多人替我想了,而我自己也有自己的理解。我要在有生之年实现一个至少能用的AI,而不只是从理论上论述它。不然,所有的研究就无意义了。我会借鉴大家的思想来完善我的思想,并以此为指导思想,而不是全盘接受或否定。 我准备从3D层面去着手人工视觉,而不是像素处理。现在有很多通过摄像头实现3D扫描建模的方法,从多角度去观察同一事物。我可以从头开始,数字图像采集方面我有尝试,这方面技术国内也有人在搞,我感觉难度并不很大。我也不需要在复杂的背景中提取物体轮阔,可以直接用纯色的背景来采集。       现在国内的技术已经能实现图像自动拼接和像素云转失量处理。这样很容易把实体变成3D模型,让机器人在自己的“虚拟空间”中“想象”和布置,形成可“感知”的环境映像。 当然,如果不考虑AI,只是研究运动,那么这个过程可以大大简化。国外有这类的技术,比如一个4轴飞行器通过视觉扫描出周围环境,并自主决定飞行路线,同时在计算机里对所感知的环境建模。具体的网上有视频。效果很好。 以目前的形势来看,这个非做不可。不需要做得很复杂很具体 。我们对一件事物的认识,有个渐近的强化过程。但强化到最后,可能却是反而是模糊的。 我也不要求一次达到多么细致的模型,只需要有大至可识别的信息量就够了。随着“观察”的深入和技术的提升,这些模型还可以继续(自主)完善。至少,让机器不再只理解“苹果是圆的” 或“画面中这个圆形区域是苹果”。 形象思维的很重要内容是“空间想象力”,我们每个人都有,正是它让我们认识这个可见的世界。技术不一定要开发,也可以获取。2000年的时候我写过3D引擎,现在更有D3D和OpenGL这样方便的API,实在想不出这有多困难。 从形象再到抽象,就是定义和描述的问题了。这至少有助于更深入理解事物和其概念。 两眼视觉不是生来就有的,是通过很长时间的训练掌握的。一般小孩子要到一定年龄才能培养出这种能力。因为他们已经从很多个角度观察了这“两幅图像”,并通过身体接触等经验将其与空间感和交互体验联系起来。可以设想,没有这种经验的人不能直接从“两幅图像”获得立体感观,而有经验的人只用一只眼也一样可以得到空间感觉。       对人类来说,这是认知过程,当然也可以直接“赋与”机器这样的能力,而减少甚至省略它的学习过程,但我更倾象于让她自己的观察中得出这样的经验。我会考虑处理灰度图像而不是彩色数据,就象狗的视觉。狗节约出来的脑细胞可以用来处理嗅觉。处理彩色图像的运算量与灰度图象的运算量相比是几乎是级数关系。可以让机器人去处理灰阶化的红外信息,很可能比处理可见光更实用。 人的视觉中枢处理的基本是集中在黄斑区的一小块图像。这非常重要。周围的信息多半用来参照和检索,以便更快定位到下一个关注点。并且那部分常常与运动系统和下意识思维相关联。 人的思维不是分时的,而是实时和并行的。人同一时刻只关注于一件事情,其他的分配给其他中枢去处理。显意识只是一部分,潜意识里有大量的计算。人是并行的多任务计算,比如人可以一边听歌一边打字。 你在和我交谈的时候,大脑的主要思维用来处理文字信息,而其他部分都是自动的,下意识甚至无意识的。AI也应该这样。 你打字的时候只想了内容,并不用想动哪个手指按哪个键,只有在刚学打字的时候才需要,形成非条件反射后,这种功能在你的语言中枢和运动中枢间直接建立了联系,而不需要显性思维,这是人脑的特点,可以分出某部分神经来处理下意识和非条件反射。语言,文字,走路,以及其他的习惯行事务都是这样。 在学习过程中,大脑找出一块未用皮层组织,进行学习性强化记忆。这时候,显性思维集中作用于这个区域,形成行为模式后(包含内容记忆)这部分相当于从门阵中建立了一个新处理器、程序与数据的统一体(新的行为模式处理机制)而不 是程序清单或数据库。它在需要的时候与大脑显意识处理区并行协同工作。学习完成后,显性思维就转移到别的地方去了,继续关注当前所面临的事务。 这非常象FPGA——一个空白的逻辑门阵列。学习时,大脑可以在其中组织出各种功能电路,包括CPU和存储器。这相当于将大脑当时的显性思维模式复制到了这个区域(自组织,自编程),使其能独立运行来处理某项具体事务而不需要显性意识的干预。未使用的皮层就相当于这种空白门阵列,学习的过程就相当于门阵列编程。FPGA有则存储器组成的查找表,保存了门单元之间的逻辑关系,大脑则通过神经细胞的突触连接完成同样的功能。 但FPGA靠查找表编程是易失的,CPLD靠熔丝编程是非易失性的。人脑则集中了二者的优点,即非易失,又可修改(完善技能或纠正错误等,类似IAP)。 用FPGA实现神经网络是现在的主流方向,我也在做相关的尝试。 武大有个老外搞这个是权威,号称人造大脑之父。他在一块FPGA上实现几个神经元然后用若干块FPGA组成阵列,这个对机器人来说有意义,但难实现,需要等待器件的升级。当FPGA的门数再提升2-3个数量级的时候,有可能实现单芯片的人工大脑。当然,也许其智能程度还没超过一只老鼠。 老万(KKND)于2011年5月16日 Dreamon-II Labs.


这两天看到各地朋友都为追踪KS-1Q信号付出了辛勤劳动,其中一直提到一款软件叫Orbitron. 由于卫星距地面高度只有几百公里,环绕速度又很快,因此对于每一处测控站,每天只有很短的几段时间能收到下行信号。Orbitron软件可以根据卫星轨道的TLE描述文件,计算卫星的飞行轨迹,再根据观测位置经纬度,计算出卫星过境的时间以及过境时的方向及仰角,为地面人员接收下行信号提供了有力保障。 Orbitron软件自带了联网同步TLE星历功能,并可以通过第三方软件驱动转台,令天线始终指向卫星。使用Orbitron软件,可以实现对单颗卫星的持续跟踪。 网上TLE数据的主要来源是space-track.org提供的API(必须登陆后使用)。Orbitron软件也支持从space-track.org更新数据,在Orbitron的设置界面找到space-track按钮,提供登录名和密码即可。如果没有账号可以去space-track网站直接注册一个,可以大大简化数据获取流程。 在使用过程中我也发现一些缺点,首先是没有命令行界面,其次是只能输出纯文本格式(而且缺少分段符)的预测报告,不方便其他程序利用。软件支持用好几种协议对外部设备进行控制,但是软件本身无法被其他软件控制。 除了Orbitron之外我还测试了GPredict。GPredict可以看做是升级版的Orbitron,通过hamlib实现转台控制,可以同时控制多个转台(包括通过TCP遥控),但是每个转台只能对准一颗星,需要手动切换。 我并不清楚科创航天在卫星项目上的目标是什么,只是有一次在宣传资料上看到说要通过多星系统实现全天候覆盖。这就提出了几个要求: 一个转台应可以跟踪多颗星,哪颗星过顶就自动对准哪颗,并切换到相应频率 整个追踪系统能够被其他程序控制 但是,由于GPredict和Orbitron都不支持被第三方程序控制(除非通过hack的方式),而且也没有直观的方法可以从软件中取出数据(除了手动保存文件,再读取),所以上面这些功能不太好实现。如果要实现这些功能,得修改GPredict软件,然后再重新编译。如果可以直接获得每一颗卫星当前的方向和仰角,那么我们就可以自己写一个简单的控制程序,让转台按照我们指定的规则工作。 要怎么根据TLE计算轨道呢? 搜索"Plan13 github"可以搜到一些项目,有一些爱好者将Plan13项目中用Basic写的轨道计算代码移植到了C语言,并编译到Arduino上,以实现轨道的离线计算。而目前能找到的最老的开源的轨道计算程序,叫"Perdict",是C语言实现的。最后,我找到Andrew West的PredictLib项目,他把Perdict移植到了JavaScript。配合这个库,我用Node写了一个HTTP服务器,可以直接根据卫星的NORAD编号和观察者的经纬度,实时计算卫星的轨道。 项目是开源的,源代码在这里: https://github.com/ctmakro/http-predict 以下为配置方式(假定你熟悉nodejs): npm install 安装dependencies 运行 node spaceTrackLoader.js ,批量下载spacetrack数据,并保存成文件。为了成功连接spacetrack,必须在config.js(从config_example.js复制一份)中填写你的spacetrack账号。 运行 node index.js ,默认监听本机3000端口,浏览器访问 <http://localhost:3000> 就可以看到效果。按照提示在地址栏输入查询指令,就可以获得实时的卫星位置,以及一天内的过境预测。这样一来,本机上运行的所有其他程序,都可以通过http协议(所有编程语言都支持)获得任意一颗卫星的实时信息。 经验证,本软件计算结果与Orbitron计算结果吻合。



nkc Development Server  https://github.com/kccd/nkc

科创研究院 (c)2005-2016

蜀ICP备11004945号-2 川公网安备51010802000058号