除却三回性将C3D文件内容全方位读取出来的那种艺术以外,还足以选拔C3DReader来一帧一帧的读取。
可怜有意思的是,VAX存款和储蓄的组织并不是按顺序存储的,而是采纳了一种叫做Middle-Endian的贮存格局来储存(并非字节序):对于四字节而言其顺序就是2301,风水节为23016745,十六字节为23016745AB89EFCD。可是全体来说,VAX浮点型与IEEE754照旧很接近的,比如VAX也要拓展规范化,不过其专业成为(0.1FFFFF..FF)2,所以上述的C就为0.5,其尾数M的范围即为贰分之一<= M <= 1 – 1 /
224;而与此同时其也并不曾明显无穷大,不须求独自为无限大留出最大的阶码,所以上述的B为0x80。
对此开创二个C3D文件,只供给利用C3DFile.Create()就能够成立贰个空的C3D文件的,不分包其余的参数集合。而保存C3D文件则直接行使file.SaveTo(“文件路径”)就足以了。
IBM双精度浮点:
添加帧能够使用如下的代码:
Byte[]
万一设数符为S,阶码为E,尾数的小数部分为F,那么能够经过位运算获得那二位:
说明
以单精度浮点数为例,假设字节查六柱预测应是之类那么些样子的,数符占第3字节的第二个人,阶码占第2字节的后八位及第一字节的第②位,其他都以尾数。
就此,有了上述的这几个消息,大家就能够将浮点数字与字节数组互相转换了(本文假定给定的字节数组都以Litten-Endian):
字节 | 类型 | 说明 |
00H | Byte | 第一个参数所在的Section在整个参数集合中的位置(通常为0x01,说明开头4字节之后就是第一个参数) |
01H | Byte | 参数集合部分标识(固定为0x50) |
02H | Byte | 参数集合所占Section数量 |
03H | Byte | 生成文件的CPU类型(0x54为Intel,0x55为DEC (VAX, PDP-11),0x56为MIPS (SGI/MIPS)) |
先是依然按字节看下VAX和IBM浮点型的贮存:
Byte
类型
是因为阶码用移码表示,所以实际的阶码则是E –
0x7F。而倒数由于是规格化的表示,即将尾数规范成为(1.FFFFF……FF)2,但只存小数点之后的局部。由于1
/ 2 + 1 / 4 + 1 / 8 + … + 1 / n = 1 – 1 /
2n,所以能够倒数M(M = 1.0 + F)的限量为1 <= M <= 2 – 1
/ 223。
Int16
上述的浮点数转字节数组无法支撑NaN和非规范化的情事,当然也得以协调判断下。当然了,下面说了这么多依然为了介绍下面二种浮点数做铺垫。假如完结系统浮点数与字节数组转换的话,用上面那种办法转换就不如用System.BitConverter来的方便人民群众了。
以下是兑现IBM浮点字节数组与系统浮点数字相互转化的类:
对此AMD和DEC生成的文书档案,都以运用Little-Endian字节序存款和储蓄的文书档案,所以一定要利用Little-Endian来读取Int1六 、Single等体系;而MIPS则选用的Big-Endian字节序存款和储蓄文书档案,所以在读取的时候势须要认清当前计算机默认的字节序以及文书档案采纳的字节序。
前不久在做C3D文件的解析,好奇怪的是文本中依旧存款和储蓄了CPU的项目,原本不屑一顾,结果后来读取1个文件发现浮点数全体读取错误。查了下发现尽管在上世纪80年间就建议了IEEE754要联合浮点数标准,可是到现行反革命照旧有总括机应用不相同方法存储浮点数。在一些非IEEE754标准的电脑爆发的二进制文件中,假使获得此外总括机中读取,假如不开始展览专门的转移,或许引致数据失实等题材。
Byte[]
当中,S为数符,它象征浮点数的正负,但与其立见成效位(尾数)是分开的。阶码用移码表示,阶码的真值都被添加3个常数(偏移量),如短实数、长实数和临时实数的偏移量用十六进制表示分别为7FH、3FFH和3FFFH。尾数部分常见皆以规格化表示,即非“0”的有效位最高位延续1。
C3D文件并未规定1个参数组前边跟另二个参数组仍然跟该参数组里的具有参数,所以读取的时候要留心下。而参数的剧情则与参数组基本均等,只是在下一参数组/参数的舞狮与参数组描述长度之间存放着该参数的实际数据罢了,由于地点描述起来太辛勤了,那里就不写了。
但是表示至十分的小的数字,允许当E = 0时非规范化的尾数存在。即当E = 0且F
!=0时,V = (-1)^S * F * A^-126。
先是说C3D文件全体不是很复杂,也并未过多扑朔迷离的概念,C3D的文书档案格式能够从其官网下载或在线阅读。首先C3D文件是以Section为单位存储的,每多个Section固定为512字节。Section一定是按顺序存款和储蓄的,可是有意思的是,Section的序号是从1上马的,而不是0。C3D文件分为三部分,分别是Section
ID = 1的C3D文件头(固定为1个Section,512字节),Section
ID一般等于2(在文书头内会付出)的C3D参数集合以及Section
ID不知道等于几(在文书头和参数集合中都会付给)的C3D数据部分。
双精度浮点数与单精度浮点数类似,只可是会扩大阶码和尾数的范围罢了。对于IEEE754的双精度浮点而言,不仅尾数的位数扩充,还会扩展阶码的尾数,字节存储如下:
在此之下就存款和储蓄着富有的参数了,参数分为两类,分别是参数组和参数。
【③ 、双精度浮点数的拍卖】
C3D文件存储了大气的参数,其利用了接近目录的法门存款和储蓄了参数,可是幸亏只有一流。即参数部分只有参数组和参数,并且每个参数组里只可以有参数不可能再包涵参数组,各种参数必须在二个参数组内。参数集合起初于文件头中的率先个字节表示的Section
ID,平时为2,但是也不肯定,有的文件会在文书头后留出空白,然后参数集合发轫的Section
ID就延期了。所以判断是或不是为C3D文件千万不要一起始读进去个Int16然后判断是还是不是0x5002,而肯定要一口咬住不放第一个字节是还是不是0x50,分明参数集合的岗位也肯定要基于文件的首先字节来。
Double S = (byte1 & 0x80) >> 7;
Double E = ((byte1 & 0x7F) << 1) + ((byte2 & 0x80) >> 7);
Double F = ((byte2 & 0x7F) << 16) + (byte3 << 8) + byte4;
字节 | 类型 | 说明 |
12AH-12BH | Int16 | 事件名是否支持4字节(支持为0x3039,不支持为0) |
12CH-12DH | Int16 | 事件数量(最大为18) |
130H-176H | Single[] | 按事件顺序存储的每个事件发生的时间(第1个帧为0.0s) |
178H-188H | Byte[] | 按事件顺序存储的每个事件是否应该显示(1为显示,0为不显示) |
18CH-1D2H | Char[] | 按事件顺序存储的每个事件的名称(每个事件占4字节) |
而IBM单精度浮点则与上述二种差异更大。首先其阶码并不是8人,而是6人,由于依旧选用移码存款和储蓄的阶码,所以其缩减的不能够是127还是128,而是64,所以其与VAX一样,也尚未无穷值的表示。除此之外,其也不是以2为底计算阶码的,而是以16为底,并且其尚无规范化尾数的供给(当然那也与其以16为底有关),所以不需求对尾数进行加减运算,其范围为1/16
<= M <= 1- 1 / 224。
以下是落到实处VAX浮点字节数组与系统浮点数字互相转化的类:
对此参数组,要存储以下6个内容:
1 using System;
2
3 namespace DotMaysWind.Numerics
4 {
5 /// <summary>
6 /// IBM单精度浮点数字
7 /// </summary>
8 /// <remarks>
9 /// SEF S EEEEEEE FFFFFFFF FFFFFFFF FFFFFFFF
10 /// bits 1 2 8 9 32
11 /// bytes byte1 byte2 byte3 byte4
12 /// </remarks>
13 public struct IBMSingle
14 {
15 #region 常量
16 private const Int32 LENGTH = 4;
17 private const Double BASE = 16.0;
18 private const Double EXPONENT_BIAS = 64.0;
19 private const Double E24 = 16777216.0;
20 #endregion
21
22 #region 字段
23 private Byte[] _data;
24 #endregion
25
26 #region 构造方法
27 /// <summary>
28 /// 初始化新的IBM单精度浮点数字
29 /// </summary>
30 /// <param name="data">IBM单精度浮点数字字节数组</param>
31 /// <param name="startIndex">数据起始位置</param>
32 public IBMSingle(Byte[] data, Int32 startIndex)
33 {
34 this._data = new Byte[IBMSingle.LENGTH];
35 Array.Copy(data, startIndex, this._data, 0, IBMSingle.LENGTH);
36 }
37
38 /// <summary>
39 /// 初始化新的IBM单精度浮点数字
40 /// </summary>
41 /// <param name="num">系统标准的单精度浮点数字</param>
42 public IBMSingle(Single num)
43 {
44 Int32 s = (num >= 0 ? 0 : 1);
45
46 Double v = Math.Abs(num);
47 Int32 e = (Int32)(Math.Log(v) / Math.Log(2.0) / 4.0 + 1.0 + IBMSingle.EXPONENT_BIAS);
48
49 Double m = (v / Math.Pow(IBMSingle.BASE, e - IBMSingle.EXPONENT_BIAS));
50 Int32 f = (Int32)(m * IBMSingle.E24);
51
52 this._data = new Byte[IBMSingle.LENGTH];
53 this._data[3] = (Byte)(s + e);
54 this._data[2] = (Byte)((f & 0x00FF0000) >> 16);
55 this._data[1] = (Byte)((f & 0x0000FF00) >> 8);
56 this._data[0] = (Byte)(f & 0x000000FF);
57 }
58 #endregion
59
60 #region 方法
61 /// <summary>
62 /// 获取系统标准的单精度浮点数字
63 /// </summary>
64 /// <returns>系统标准的单精度浮点数字</returns>
65 public Single ToSingle()
66 {
67 Byte b1 = this._data[3];
68 Byte b2 = this._data[2];
69 Byte b3 = this._data[1];
70 Byte b4 = this._data[0];
71
72 Double s = (b1 & 0x80) >> 7;
73 Double e = (b1 & 0x7F);
74 Double f = (b2 << 16) + (b3 << 8) + b4;
75 Double m = f / IBMSingle.E24;
76
77 if (e == 0 && f == 0 && s == 0) return 0;
78
79 return (Single)((s == 0 ? 1.0 : -1.0) * m * Math.Pow(IBMSingle.BASE, e - IBMSingle.EXPONENT_BIAS));
80 }
81
82 /// <summary>
83 /// 获取IBM单精度浮点数据字节数组
84 /// </summary>
85 /// <returns>字节数组</returns>
86 public Byte[] ToArray()
87 {
88 Byte[] data = new Byte[IBMSingle.LENGTH];
89
90 Array.Copy(this._data, data, IBMSingle.LENGTH);
91
92 return data;
93 }
94 #endregion
95 }
96 }
从而,当E = 0xFF时,指数最大,规定F = 0时为无穷值,个中又有S =
0为正无穷以及S = 1为负无穷;而F != 0时为无效数字(NaN)。
参数存放内容的类型(-1 Char,1 Byte,2
Int16,4 Single),相对值即为长度
【壹 、C3D文件格式的组织】
可知,浮点数就不存在0的定义了,所以不得不用极端小来代表,同时为了表示无穷大,规定E取值范围为0
< E < 0xFF,即-0x7F < (E – B) < 0x80。
View Code
字节 | 类型 | 说明 |
00H | Byte | 参数集合开始的Section ID(通常为0x02,但也不一定) |
01H | Byte | 文件标识(固定为0x50) |
02H-03H | Int16 | 每帧里3D坐标点的个数 |
04H-05H | Int16 | 每帧里模拟测量的个数 |
06H-07H | Int16 | 第1帧的序号(最小为1) |
08H-09H | Int16 | 最后1帧的序号 |
0AH-0BH | Int16 | 最大插值差距 |
0CH-0FH | Single | 比例因子(正数表示存储的帧为整数帧,负数为浮点帧) |
10H-11H | Int16 | 数据区域开始的Section ID |
12H-13H | Int16 | 每帧模拟采样个数 |
14H-17H | Single | 帧率 |
足见,其阶码扩展了三位,即最大值是原本翻了3翻,为1024。而为了确定保证能代表无穷值,所以B为1023。除此之外只需求多读取前面增添的尾数即可,步骤与单精度基本相同。
1 C3DFile file = C3DFile.LoadFromFile("文件路径");
2 Int16 firstFrameIndex = file.Header.FirstFrameIndex;
3 Int16 pointCount = file.Parameters["POINT:USED"].GetData<Int16>();
4
5 for (Int16 i = 0; i < file.AllFrames.Count; i++)
6 {
7 for (Int16 j = 0; j < pointCount; j++)
8 {
9 Console.WriteLine("Frame {0} : X = {1}, Y = {2}, Z = {3}",
10 firstFrameIndex + i,
11 file.AllFrames[i].Point3Ds[j].X,
12 file.AllFrames[i].Point3Ds[j].Y ,
13 file.AllFrames[i].Point3Ds[j].Z);
14 }
15 }
SEF S EEEEEEEE FFFFFFF FFFFFFFF FFFFFFFF
bits 1 2 9 10 32
bytes byte2 byte3 byte0 byte1
另2个是C3D文件能在不一样门类的CPU上变化,那显示于差异CPU也许行使的字节序(Endian)和浮点数字不一致,比如大家用的CPU都以使用Little-Endian以及IEEE754的浮点数标准。从网上查还发现有DEC
(VAX)以及IBM等CPU选拔分歧的浮点数标准,详见笔者事先一篇文章:http://www.cnblogs.com/mayswind/p/3365594.html。而C3D则是永葆3类CPU,速龙CPU选取Little-Endian以及IEEE754标准的浮点数,DEC
(VAX)接纳的Little-Endian以及故意的浮点数,MIPS
(SGI)选择的Big-Endian以及IEEE754标准的浮点数,所以在读取文书档案的时候可能须要10分开始展览拍卖,在第一节会详细表明。
SEF S EEEEEEE EEEE FFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
bits 1 2 12 13 64
bytes byte1 byte2 byte3 byte4 byte5 byte6 byte7 byte8
VAX单精度浮点:
- C3D.ORG:http://www.c3d.org/
- c3d4sharp – C3D File reading/writing tools written in
C#:http://code.google.com/p/c3d4sharp/ - C3D.NET:https://c3d.codeplex.com/
而对于VAX和IBM的双精度浮点,更是没有扩充阶码的限量,而只是扩大了尾数的限定,使得只要多读取增添的几个人尾数即可,而常数A、B、C更是无需修改。两者字节存款和储蓄如下:
【贰 、C3D文件头的协会】
SEF S EEEEEEEE FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
bits 1 2 9 10 64
bytes byte2 byte3 byte0 byte1 byte6 byte7 byte4 byte5
1 Single frameRate = file.Parameters["POINT", "RATE"].GetData<Single>();
2 Int16 analogChannelCount = file.Parameters["ANALOG", "USED"].GetData<Int16>();
3 Int16 analogSamplesPerFrame = (Int16)(file.Parameters["ANALOG", "RATE"].GetData<Int16>() / frameRate);
4
5 for (Int16 i = 0; i < file.AllFrames.Count; i++)
6 {
7 for (Int16 j = 0; j < analogChannelCount; j++)
8 {
9 for (Int16 k = 0; k < analogSamplesPerFrame; k++)
10 {
11 Console.WriteLine("Frame {0}, Sample {1} : {2}",
12 firstFrameIndex + i, j + 1,
13 file.AllFrames[i].AnalogSamples[j][k]);
14 }
15 }
16 }
【题外话】
当E = 0时,指数最小,规定F = 0时为0,在这之中又有S = 0为正0以及S = 1时为-0。
率先来说第2局地,也便是C3D的公文头,C3D的公文头一定只占三个Section,即一定的512字节,所以假如读取前512字节就足以把方方面面头数据获得到了。固然各种Section有512字节之多,但是对于C3D的公文头只占了很少的一片段,在文件头中有多量空白的区域。个中第壹部分是文本头参数部分,内容如下:
事先的剧情
View Code
参数每一维大小(倘使维数为0,就不曾此部分)
VAX双精度浮点:
1 file.AllFrames.Add(new C3DFrame(new C3DPoint3DData[] {
2 new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
3 new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
4 new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
5 new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
6 new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask} }));
【相关链接】
不久前实验室要作者修改C3D(The 3D Biomechanics Data
Standard)文件,固然从网上找到了二个叫c3d4sharp的类库,这些类库单纯读取C3D文件的话还是能,可是一旦要促成修改或然创设C3D文件就比较麻烦了。同时c3d4sharp完成得相比较简单,很多C3D文件里一些数据都不扶助。幸而C3D文件总体不是很复杂,于是小编就起头重新写了叁个C3D文件读写的库,未来在codeplex上成立了个类型叫C3D.NET。
SEF S EEEEEEE FFFFFFFF FFFFFFFF FFFFFFFF
bits 1 2 8 9 32
bytes byte1 byte2 byte3 byte4
而读取模拟采集样品的话,选拔的艺术也就像:
在此之后的第③某些,也正是储存的风浪,听上去应该占很多字节,不过出于限制了风浪数量最多不能够超越二十三个,同时事件名称最长为4字节,所以事件部分也只占很少的半空中。由于C3D首假如为着记录运动的数目,或者在其间有诸多相比根本的地方,事件正是用来标记出这一个地点的。3个事变包含八个内容,分别是最长四字节的轩然大波名称、一字节的事件是不是应当显得的情事以及四个四字节的单精度浮点数表示事件出现的小时。
之所以可经过如下的公式来计量浮点数的值,当中,C是倒数规范化后回落的常量,B是移码的偏移量,可见A、B、C分别为A
= ② 、B = 0x7F以及C = 1.0。
- 对于每帧的3D坐标点部分,存款和储蓄着该帧全部3D坐标点的数目,各类3D坐标点包含6个Int16或Single数据,分别是X坐标、Y坐标、Z坐标以及Residual和Camera
Mask,当中Residual和Camera
Mask共占2个Int16。相比好玩的是,对于浮点帧,Residual和Camera
Mask还是也照旧3个Int16,只可是存款和储蓄的时候要将相应的数值转换为Single再开始展览仓储。- 对此浮点帧,存款和储蓄的X、Y、Z坐标正是其实的坐标;而对此整数帧,存款和储蓄的X、Y、Z的坐标还须要倍加比例因子才能够,比例因子存款和储蓄于参数集合中的”POINT”参数组中的”SCALE”参数。
- Residual和Camera
Mask共占三个Int16,将其转移为字节数组之后,高位字节(第一个字节)的最高位代表Residual的符号,即意味着该坐标点是还是不是有效,假使为0则代表有效,借使为1则意味着无效,而剩余的几个字节则为Camera
Mask,每壹人表示八个水墨画机,从没有到高位分别代表柒个录制机是还是不是采纳(为1为运用,为0为未采用)。而Residual的真实数据则为字节数组的第0字节乘以比例因子(浮点帧则为比例因子的相对值)。
-
而模仿采集样品部分,则存款和储蓄着该帧全数的模仿采集样品的数额,但是每一个帧恐怕包蕴四个模拟采集样品,同时每一个模仿采集样品恐怕又蕴涵七个channel,存款和储蓄的数目即为该channel下记录的数码。可是存款和储蓄的数额与事实上的多少还索要遵照下述公式实行折算,在那之中data
value为存款和储蓄的数量,real world value为实际的数量。- zero
offset能够从”ANALOG”参数组中的”OFFSET”中赢得,该数额为Int16的数组,第i位指的便是第i个channel的zero
offset。 - channel
scale能够从”ANALOG”参数组中的”SCALE”中取得,该数量为Single的数组,第i位指的正是dii个channel的scale。 - general
scale是独具模拟采样都亟需倍加的比例,该多少年足球以从”ANALOG”参数组中的”GEN_SCALE”中获取,为Single。
real world value = (data value – zero offset) channel scale general scale
- zero
二进制表示 | 十六进制表示 | 含义 | 十进制表示 |
0 11111111 00000000000000000000000 | 7F 80 00 00 | 正无穷 | +∞ |
1 11111111 00000000000000000000000 | FF 80 00 00 | 负无穷 | -∞ |
0 00000000 00000000000000000000000 | 00 00 00 00 | +0 | 0 |
1 00000000 00000000000000000000000 | 80 00 00 00 | -0 | 0 |
0 00000000 00000000000000000000001 | 00 00 00 01 | 最小正数 | 1.401298E-45 |
0 11111110 11111111111111111111111 | 7F 7F FF FF | 最大值 | 3.402823E+38 |
1 11111110 11111111111111111111111 | FF 7F FF FF | 最小值 | -3.402823E+38 |
0 01111111 00000000000000000000000 | 3F 80 00 00 | +1 | 1 |
SEF S EEEEEEE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
bits 1 2 8 9 64
bytes byte1 byte2 byte3 byte4 byte5 byte6 byte7 byte8
【小说索引】
前方说了如此多,其实倘若用C3D.NET来分析的话实际是万分不难的。大家能够从https://c3d.codeplex.com/下载C3D.NET的二进制文件或然源码,引用后主要的类都在C3D这么些命名空间下。
【壹 、IEEE754标准浮点数字的蕴藏详解】
【② 、VAX及IBM浮点数字的积存和转移】
【③ 、C3D文件参数集合的组织】
SEF S EEEEEEEE FFFFFFF FFFFFFFF FFFFFFFF
bits 1 2 9 10 32
bytes byte1 byte2 byte3 byte4
Byte
1 using System;
2
3 namespace DotMaysWind.Numerics
4 {
5 /// <summary>
6 /// VAX单精度浮点数字
7 /// </summary>
8 /// <remarks>
9 /// SEF S EEEEEEEE FFFFFFF FFFFFFFF FFFFFFFF
10 /// bits 1 2 9 10 32
11 /// bytes byte2 byte1 byte4 byte3
12 /// </remarks>
13 public struct VAXSingle
14 {
15 #region 常量
16 private const Int32 LENGTH = 4;
17 private const Double BASE = 2.0;
18 private const Double EXPONENT_BIAS = 128.0;
19 private const Double MANTISSA_CONSTANT = 0.5;
20 private const Double E24 = 16777216.0;
21 #endregion
22
23 #region 字段
24 private Byte[] _data;
25 #endregion
26
27 #region 构造方法
28 /// <summary>
29 /// 初始化新的VAX单精度浮点数字
30 /// </summary>
31 /// <param name="data">VAX单精度浮点数字字节数组</param>
32 /// <param name="startIndex">数据起始位置</param>
33 public VAXSingle(Byte[] data, Int32 startIndex)
34 {
35 this._data = new Byte[VAXSingle.LENGTH];
36 Array.Copy(data, startIndex, this._data, 0, VAXSingle.LENGTH);
37 }
38
39 /// <summary>
40 /// 初始化新的VAX单精度浮点数字
41 /// </summary>
42 /// <param name="num">系统标准的单精度浮点数字</param>
43 public VAXSingle(Single num)
44 {
45 Int32 s = (num >= 0 ? 0 : 1);
46
47 Double v = Math.Abs(num);
48 Int32 e = (Int32)(Math.Log(v) / Math.Log(2.0) + 1.0 + VAXSingle.EXPONENT_BIAS);
49
50 Double m = (v / Math.Pow(VAXSingle.BASE, e - VAXSingle.EXPONENT_BIAS)) - VAXSingle.MANTISSA_CONSTANT;
51 Int32 f = (Int32)(m * VAXSingle.E24);
52
53 this._data = new Byte[VAXSingle.LENGTH];
54 this._data[1] = (Byte)((s << 7) + ((e & 0xFE) >> 1));
55 this._data[0] = (Byte)(((e & 0x01) << 7) + ((f & 0x007F0000) >> 16));
56 this._data[3] = (Byte)((f & 0x0000FF00) >> 8);
57 this._data[2] = (Byte)(f & 0x000000FF);
58 }
59 #endregion
60
61 #region 方法
62 /// <summary>
63 /// 获取系统标准的单精度浮点数字
64 /// </summary>
65 /// <returns>系统标准的单精度浮点数字</returns>
66 public Single ToSingle()
67 {
68 Byte b1 = this._data[1];
69 Byte b2 = this._data[0];
70 Byte b3 = this._data[3];
71 Byte b4 = this._data[2];
72
73 Double s = (b1 & 0x80) >> 7;
74 Double e = ((b1 & 0x7F) << 1) + ((b2 & 0x80) >> 7);
75 Double f = ((b2 & 0x7F) << 16) + (b3 << 8) + b4;
76 Double m = f / VAXSingle.E24;
77
78 if (e == 0 && s == 0) return 0;
79 if (e == 0 && s == 1) return Single.NaN;
80
81 return (Single)((s == 0 ? 1.0 : -1.0) * (VAXSingle.MANTISSA_CONSTANT + m) * Math.Pow(VAXSingle.BASE, e - VAXSingle.EXPONENT_BIAS));
82 }
83
84 /// <summary>
85 /// 获取VAX单精度浮点数据字节数组
86 /// </summary>
87 /// <returns>字节数组</returns>
88 public Byte[] ToArray()
89 {
90 Byte[] data = new Byte[VAXSingle.LENGTH];
91
92 Array.Copy(this._data, data, VAXSingle.LENGTH);
93
94 return data;
95 }
96 #endregion
97 }
98 }
【题外话】
不过C3D也有很复杂的地点,二个是关于整型的接纳,能够选取应用有号子的(Int16),也足以使用无符号的(UInt16),只可是后者能积存的数据量要多一些罢了,既然那样,不知为何当初还要采纳有标志的整型。而且最重点的是,文书档案内没有别的标识能提出文书档案使用的是何种整型。官方给出的缓解措施是,能够依据例如帧总数、帧索引等判断,倘若读出负数,则使用无符号的,不然采取有号子的。
而二进制小数转十进制小数的总结能够直接按整数的转换成做,然后除以2n即可,n在此处实在正是尾数的长短,为23。
【伍 、使用C3D.NET读写文件示例】
1 Single ToSingle(Byte[] data)
2 {
3 Double a = 2.0;
4 Double b = 127.0;
5 Double c = 1.0;
6 Double d = -126.0;
7
8 Byte byte1 = data[3];
9 Byte byte2 = data[2];
10 Byte byte3 = data[1];
11 Byte byte4 = data[0];
12
13 Double s = (byte1 & 0x80) >> 7;
14 Double e = ((byte1 & 0x7F) << 1) + ((byte2 & 0x80) >> 7);
15 Double f = ((byte2 & 0x7F) << 16) + (byte3 << 8) + byte4;
16 Double m = f / Math.Pow(2, 23);
17
18 if (e == 0xFF && f == 0) return (s == 0 ? Single.PositiveInfinity : Single.NegativeInfinity);
19 if (e == 0xFF && f != 0) return Single.NaN;
20 if (e == 0x00 && f == 0) return 0;
21 if (e == 0x00 && f != 0) return (Single)((s == 0 ? 1.0 : -1.0) * m * Math.Pow(a, d));
22
23 return (Single)((s == 0 ? 1.0 : -1.0) * (c + m) * Math.Pow(a, e - b));
24 }
25
26 Byte[] GetBytes(Single num)
27 {
28 Double a = 2.0;
29 Double b = 127.0;
30 Double c = 1.0;
31 Double d = Math.Log(2);
32
33 Int32 s = (num >= 0 ? 0 : 1);
34
35 Double v = Math.Abs(num);
36 Int32 e = (Int32)(Math.Log(v) / d + b);
37
38 Double m = (v / Math.Pow(a, e - b)) - c;
39 Int32 f = (Int32)(m * Math.Pow(2, 23));
40
41 Byte[] data = new Byte[4];
42 data[3] = (Byte)((s << 7) + ((e & 0xFE) >> 1));
43 data[2] = (Byte)(((e & 0x01) << 7) + ((f & 0x007F0000) >> 16));
44 data[1] = (Byte)((f & 0x0000FF00) >> 8);
45 data[0] = (Byte)(f & 0x000000FF);
46
47 return data;
48 }
参数组描述长度
- Transform between IEEE, IBM or VAX floating point number formats and
bytes
expressions:http://www.codeproject.com/Articles/12363/Transform-between-IEEE-IBM-or-VAX-floating-point-n - VAX F_FLOAT and D_FLOAT to IEEE T_FLOAT and S_FLOAT
(double):http://yaadc.blogspot.com/2013/01/vax-ffloat-and-dfloat-to-ieee-tfloat.html - IEEE
Arithmetic:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_math.html - Floating-Point:http://andromeda.rutgers.edu/~dupre/231/lecture13.doc
- IBM Floating Point
Architecture:http://en.wikipedia.org/wiki/IBM_Floating_Point_Architecture - VAX floating point to
Decimal:http://arstechnica.com/civis/viewtopic.php?f=20&t=171682
然则须要注明的是,对于Char[]以及Char[,]那三种,假若表示的话实际应该相应的是String以及String[]。
IBM单精度浮点:
参数实际内容
V = (-1)^S * (F + C) * A^(E - B)
Byte
对于x86等普遍的CPU,都以应用IEEE754存储和计算浮点型的,当然在.NET平布里斯托浮点型也是IEEE754标准的。首先回看下本科时学过的电脑组成原理,查了下课本发现是之类介绍IEEE754浮点数的存款和储蓄的(唐朔飞版课本233页):
而对于英特尔和MIPS生成的文书档案,对于浮点数字的存款和储蓄都以选择正式的IEEE754浮点数字,对于.NET而言不须要实行其余处理;而DEC生成的文书档案则应用特有浮点数,须求将五个字节全体读取以往再拓展分外规的转移,转换方法见自身事先的小说:http://www.cnblogs.com/mayswind/p/3365594.html。
字节 | 类型 | 说明 |
00H | SByte | 参数组名称长度(如果为负数则表示该参数组锁定请不要修改,而长度为绝对值) |
01H | SByte | 参数组ID的负数 |
02H – … | Char[] | 参数组名称(仅包含大写字母、0-9以及下划线_) |
… + 1 – … + 2 | Int16 | 下一参数组/参数的偏移(包含本内容的2字节) |
… + 3 | Byte | 参数组描述长度 |
… + 4 – | Char[] | 参数组描述内容(ASCII码) |
参数内容维数(0-3)
而对于参数集合,发轫的4字节概念如下:
本来,也足以将C3DPoint3DData数组换到C3DAnalog萨姆ples数组,大概双方同时丰硕也得以。
数据区域开端于参数集合中的”POINT”参数组中的”DATA_STA安德拉T”参数,其表示数据区域发轫的Section
ID,除此之外,在文件头中也有一份副本。然则根据官方的布道,假如文件头和参数集合中都局部内容,优先读取参数集合中的数据。
下一参数组/参数的晃动(包涵本内容的2字节)
【④ 、C3D文件数量区域的构造】
从此的始末
1 using (FileStream fs = new FileStream("文件路径", FileMode.Open, FileAccess.Read))
2 {
3 C3DReader reader = new C3DReader(fs);
4 C3DHeader header = reader.ReadHeader();
5 C3DParameterDictionary dictionary = reader.ReadParameters();
6 Int32 index = header.FirstFrameIndex;
7
8 while (true)
9 {
10 C3DFrame frame = reader.ReadNextFrame(dictionary);
11
12 if (frame == null)
13 {
14 break;
15 }
16
17 for (Int16 j = 0; j < frame.Point3Ds.Length; j++)
18 {
19 Console.WriteLine("Frame {0} : X = {1}, Y = {2}, Z = {3}",
20 index++,
21 frame.Point3Ds[j].X,
22 frame.Point3Ds[j].Y,
23 frame.Point3Ds[j].Z);
24 }
25 }
26 }
对此各个帧,又含有多少个部分,第贰有的为3D坐标点部分,第2局地为模拟采集样品部分。
对此增长参数集合能够使用以下的代码:
【小说索引】
此间要求验证的就是,由于参数能够存放数组,所以扩张了维数的标识,即当维数为0时,存放的剧情为Char、Byte、Int1⑥ 、Single等转移出的字节数组;而当维数为1时,存放的为Char[]、Byte[]、Int16[]、Single[]等转移出的字节数组,以此类推。而对数组的储存,其实正是数组各类成分依次展打开仓库储,而对此多维数组,则是按行优先开始展览仓库储存的,比如三维数组,先存款和储蓄Data[0,0,1]再存储Data[0,0,2],依次类推。
1 //首先需要添加参数集合,ID为正数
2 file.Parameters.AddGroup(1, "POINT", "");
3 //然后往指定ID的参数集合中添加参数即可
4 file.Parameters[1].Add("USED", "").SetData<Int16>(5);
对于遍历全体的3D坐标能够采纳以下的主意,首先能够从文件恐怕从流中创设C3D文件,然后从文件头中读取存款和储蓄的第贰帧的序号,然后读取采集样品点的数量就足以了,当然也能够不从参数组中读取,直接行使file.AllFrames[i].Point3Ds.Length也可以:
字节
【相关链接】
其间前1个字节官方说一向忽略就行,可是为了合营在写入的时候依旧要写进去的。第②字节其实我们按梯次读到头也不须要那个数额。那其间重点的是CPU类型,由于不相同CPU类型选取的字节序以及存款和储蓄的浮点数字有所不一致,所以大家还亟需基于CPU类型实行对应的处理。
C3D数据区域以帧为单位寄放的,其实一定于那几个区域正是二个帧的聚众。而C3D帧其实分为二种,一种是整数帧,而另一种是浮点帧。那三头的区分在于,前者存款和储蓄的富有情节都是Int16,而后人则为Single,除此之外,前者的3D坐标点(X、Y、Z)还索要倍加比例因子才能够,而后人存款和储蓄的始末也就是已经乘以了百分比因子了。