Subversion Repositories Projects

Rev

Rev 1689 | Rev 1695 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1563 - 1
package dongfang.mkt.comm;
2
 
3
import java.io.IOException;
4
import java.io.InputStream;
5
import java.io.OutputStream;
6
 
7
import dongfang.mkt.datatype.GPSBearingAndRange;
8
import dongfang.mkt.datatype.GPSPosition;
9
import dongfang.mkt.frames.AllDisplaysResponseFrame;
10
import dongfang.mkt.frames.AnalogDebugLabelResponseFrame;
11
import dongfang.mkt.frames.AttitudeDataResponseFrame;
12
import dongfang.mkt.frames.ChangeParameterSetResponseFrame;
13
import dongfang.mkt.frames.CompassHeadingResponseFrame;
14
import dongfang.mkt.frames.ConfirmFrame;
15
import dongfang.mkt.frames.DebugResponseFrame;
16
import dongfang.mkt.frames.MotorTestResponseFrame;
17
import dongfang.mkt.frames.OSDDataResponseFrame;
18
import dongfang.mkt.frames.ReadExternalControlResponseFrame;
1611 - 19
import dongfang.mkt.frames.ReadIMUConfigurationResponseFrame;
1688 - 20
import dongfang.mkt.frames.ReadMotorMixerResponseFrame;
1690 - 21
import dongfang.mkt.frames.ReadParamSetResponseFrame;
22
import dongfang.mkt.frames.ReadRCChannelsResponseFrame;
23
import dongfang.mkt.frames.ReadVariablesResponseFrame;
1563 - 24
import dongfang.mkt.frames.ResponseFrame;
25
import dongfang.mkt.frames.SetCompassHeadingResponseFrame;
1690 - 26
import dongfang.mkt.frames.VersionResponseFrame;
1611 - 27
import dongfang.mkt.frames.WriteIMUConfigurationResponseFrame;
1688 - 28
import dongfang.mkt.frames.WriteMotorMixerResponseFrame;
1611 - 29
import dongfang.mkt.frames.WriteParamSetResponseFrame;
1563 - 30
 
31
public class MKInputStream extends InputStream {
32
	int readByteCnt;
33
	class MKDataInputStream {
34
		int[] inbuf = new int[4];
35
		int[] outbuf = new int[3];
36
		int outbufptr = outbuf.length; // reset to "buffer empty"
37
 
38
		private boolean decode() throws IOException {
39
			for (int i = 0; i < 4; i++) {
40
				int raw = MKInputStream.this.readByte();
41
				int in = raw - '=';
42
				if (in < 0 || in > 63)
43
					return false;
44
					// throw new IOException("Out of range data received where frame data expected. Probably the frame was shorter than expected (" + readByteCnt + ")!");
45
				inbuf[i] = in;
46
				readByteCnt++;
47
			}
48
			outbuf[0] = (inbuf[0] << 2) | (inbuf[1] >>> 4);
49
			outbuf[1] = ((inbuf[1] & 0x0f) << 4) | (inbuf[2] >>> 2);
50
			outbuf[2] = ((inbuf[2] & 0x03) << 6) | inbuf[3];
51
			outbufptr = 0;
52
			return true;
53
		}
54
 
55
		public void reset() {
56
			outbufptr = outbuf.length; // reset to "buffer empty"
57
		}
58
 
59
		public int readByte() throws IOException {
60
			if (outbufptr > 2 && !decode())
61
					throw new IOException("Out of range data received where frame data expected. Probably the frame was shorter than expected (" + readByteCnt + ")!");
62
			return outbuf[outbufptr++];
63
		}
64
 
65
		public int readSignedByte() throws IOException {
66
			byte result = (byte)readByte();
67
			return result;
68
		}
69
 
70
		public int readWord() throws IOException {
71
			int byte0 = readByte();
72
			int byte1 = readByte();
73
			return (byte1 << 8) | byte0;
74
		}
75
 
76
		public int readSignedWord() throws IOException {
77
			int word = readWord();
78
			if (word > 32767)
79
				word = word - 65536;
80
			return word;
81
		}
82
 
83
		public int readSignedDWord() throws IOException {
84
			int byte0 = readByte();
85
			int byte1 = readByte();
86
			int byte2 = readByte();
87
			int byte3 = readByte();
88
			return (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0;
89
		}
90
 
91
		public int[] readBytes(int length) throws IOException {
92
			int[] result = new int[length];
93
			for (int i = 0; i < length; i++) {
94
				result[i] = readByte();
95
			}
96
			return result;
97
		}
98
 
99
		public int[] readWords(int length) throws IOException {
100
			int[] result = new int[length];
101
			for (int i = 0; i < length; i++) {
102
				result[i] = readWord();
103
			}
104
			return result;
105
		}
106
 
107
		public int[] readSignedWords(int length) throws IOException {
108
			int[] result = new int[length];
109
			for (int i = 0; i < length; i++) {
110
				result[i] = readSignedWord();
111
			}
112
			return result;
113
		}
114
 
115
		public char[] readChars(int length) throws IOException {
116
			char[] result = new char[length];
117
			for (int i = 0; i < length; i++) {
118
				// Here, a 1:1 mapping between byte values and char codes is assumed.
119
				// That means we're assuming ISO-8859-1 (= the first 256 code points
120
				// of Unicode, which Java uses for chars)
121
				result[i] = (char) readByte();
122
			}
123
			return result;
124
		}
125
	}
126
 
127
	MKDataInputStream base64InputStream = new MKDataInputStream();
128
	OutputStream nonPacketSpillway = null; //System.err;
129
 
130
	final InputStream is;
131
	int crc;
132
 
133
	public MKInputStream(InputStream is) {
134
		this.is = is;
135
	}
136
 
137
	@Override
138
	public int read() throws IOException {
139
		int i;
140
		while ((i=is.read()) == -1);
141
		// System.out.print("Received: " + i + " (as char: " + (char)i + ")\n");
142
		return i;
143
	}
144
 
145
	public int readByte() throws IOException {
146
		int _byte = read();
147
		if (_byte < 0)
148
			throw new IOException("End of Stream!");
149
		crc += _byte;
150
		return _byte;
151
	}
152
 
153
	public MKDataInputStream getBase64InputStream() {
154
		return base64InputStream;
155
	}
156
 
157
	public ResponseFrame getNextFrame() throws IOException {
158
		int c;
159
		while ((c = read()) != '#') {
160
			// throw it on some scrap-text buffer.
161
			if (nonPacketSpillway != null)
162
				nonPacketSpillway.write(c);
163
		}
164
		crc = '#';
165
		base64InputStream.reset();
166
		int address = readByte() - 'a';
167
		int iid = readByte();
168
		readByteCnt = 0;
169
		//RESPONSE_IDS id = getResponseType(iid);
170
		ResponseFrame result;
171
//		System.out.println("Received a: " + (char)iid + " from " + address);
172
		switch (iid) {
173
		case 'A': {
174
			AnalogDebugLabelResponseFrame f = new AnalogDebugLabelResponseFrame(address);
175
			f.setChannel(base64InputStream.readByte());
176
			f.setLabel(base64InputStream.readChars(16));
177
			result = f;
178
			break;
179
		}
180
		case 'B': {
181
			ConfirmFrame f = new ConfirmFrame(address);
182
			f.setFrameNum(base64InputStream.readByte());
183
			result = f;
184
			break;
185
		}
186
		case 'C': {
187
			AttitudeDataResponseFrame f = new AttitudeDataResponseFrame(address);
188
			f.setPitch(base64InputStream.readSignedWord());
189
			f.setRoll(base64InputStream.readSignedWord());
190
			f.setHeading(base64InputStream.readSignedWord());
191
			f.setExpansion(base64InputStream.readBytes(8));
192
			result = f;
193
			break;
194
		}
195
		case 'D': {
196
			DebugResponseFrame f = new DebugResponseFrame(address);
197
			for (int i=0; i<2; i++)
198
				f.setDigital(i, base64InputStream.readByte());
199
			for (int i=0; i<32; i++)
200
				f.setAnalog(i, base64InputStream.readSignedWord());
201
			result = f;
202
			break;
203
		}
204
		case 'F': {
205
			ChangeParameterSetResponseFrame f = new ChangeParameterSetResponseFrame(address);
206
			f.setParameterSetNumber(base64InputStream.readByte());
207
			result = f;
208
			break;
209
		}
210
		case 'G': {
211
			ReadExternalControlResponseFrame f = new ReadExternalControlResponseFrame(address);
212
			f.setDigital(base64InputStream.readBytes(2));
213
			f.setRemoteButtons(base64InputStream.readByte());
214
			f.setPitch(base64InputStream.readByte());
215
			f.setRoll(base64InputStream.readByte());
216
			f.setYaw(base64InputStream.readByte());
217
			f.setThrottle(base64InputStream.readByte());
218
			f.setHeight(base64InputStream.readByte());
219
			f.setCommand(base64InputStream.readByte());
220
			f.setFrameNum(base64InputStream.readByte());
221
			f.setArgument(base64InputStream.readByte());
222
			result = f;
223
			break;
224
		}
225
		case 'H': {
226
			AllDisplaysResponseFrame f = new AllDisplaysResponseFrame(address);
227
			f.setLine(base64InputStream.readByte());
228
			//f.setMaxItem(getDataInputStream().readByte());
229
			f.setText(base64InputStream.readChars(20));
230
			result = f;
231
			break;
232
		}
1611 - 233
		case 'I': {
234
			ReadIMUConfigurationResponseFrame f = new ReadIMUConfigurationResponseFrame(address);
235
			f.setConfigurationVersion(base64InputStream.readByte());
236
			int length = base64InputStream.readByte();
237
			f.setConfigurationSetLength(length);
238
			f.setData(base64InputStream.readBytes(length));
239
			result = f;
240
			break;
241
		}
242
		case 'J': {
243
			WriteIMUConfigurationResponseFrame f = new WriteIMUConfigurationResponseFrame(address);
244
			f.setWasAccepted(base64InputStream.readByte()==1);
245
			result = f;
246
			break;
247
		}
1563 - 248
		case 'k' : {
249
			CompassHeadingResponseFrame f = new CompassHeadingResponseFrame(address);
250
			base64InputStream.readSignedWords(2);
251
			base64InputStream.readBytes(2);
252
			base64InputStream.readByte();
253
			base64InputStream.readByte();
254
			result = f;
255
			break;
256
		}
257
		case 'L': {
258
			AllDisplaysResponseFrame f = new AllDisplaysResponseFrame(address);
259
			f.setItem(base64InputStream.readByte());
260
			// f.setMaxItem(getDataInputStream().readByte());
261
			f.setText(base64InputStream.readChars(80));
262
			result = f;
263
			break;
264
		}
1688 - 265
		case 'M': {
266
			WriteMotorMixerResponseFrame f = new WriteMotorMixerResponseFrame(address);
267
			f.setWasAccepted(base64InputStream.readByte() != 0);
1689 - 268
			result = f;
269
			break;
1688 - 270
		}
271
		case 'N': {
272
			int numMotors = 12;
273
			ReadMotorMixerResponseFrame f = new ReadMotorMixerResponseFrame();
274
			f.setConfigurationVersion(base64InputStream.readByte());
275
			int length = base64InputStream.readByte();
276
			f.setDataLength(length);
1689 - 277
			int testLength = 12; // the name is first!
278
			char[] name = base64InputStream.readChars(12);
279
			f.setName(name);
1688 - 280
			int[][] matrix = new int[numMotors][];
281
			for(int i=0; i<numMotors; i++) {
1689 - 282
				int[] row = new int[5];
283
				matrix[i] = row;
284
				for(int j=0; j<5; j++) {
285
					row[j] = base64InputStream.readSignedByte();
1688 - 286
					testLength++;
287
				}
288
			}
289
 
290
			if (length != testLength)
1689 - 291
				throw new IOException("Length of motor mixer data was not as expected (" + testLength + " vs. " + length + ").");
1688 - 292
			/*
293
			int[] opposite = new int[numMotors];
294
			for(int i=0; i<numMotors; i++) {
295
				opposite[i] = base64InputStream.readByte();
296
			}
297
			*/
298
			f.setMatrix(matrix);
299
			result = f;
1689 - 300
			break;
1688 - 301
		}
1563 - 302
		case 'O': {
303
			OSDDataResponseFrame f = new OSDDataResponseFrame(address);
304
			f.setVersion(base64InputStream.readByte());
305
 
306
			GPSPosition pos = new GPSPosition();
1565 - 307
			pos.setLongitude(((double)base64InputStream.readSignedDWord())/1E7);
308
			pos.setLatitude(((double)base64InputStream.readSignedDWord())/1E7);
309
			pos.setAltitude(((double)base64InputStream.readSignedDWord())/1E3);
1563 - 310
			pos.setStatus(base64InputStream.readByte());
311
			f.setCurrentPosition(pos);
312
 
313
			pos = new GPSPosition();
1565 - 314
			pos.setLongitude(((double)base64InputStream.readSignedDWord())/1E7);
315
			pos.setLatitude(((double)base64InputStream.readSignedDWord())/1E7);
316
			pos.setAltitude(((double)base64InputStream.readSignedDWord())/1E3);
1563 - 317
			pos.setStatus(base64InputStream.readByte());
318
			f.setTargetPosition(pos);
319
 
320
			GPSBearingAndRange rnb = new GPSBearingAndRange();
1565 - 321
			rnb.setDistance(((double)base64InputStream.readWord())/10.0);
1563 - 322
			rnb.setBearing(base64InputStream.readSignedWord());
323
			f.setCurrentToTarget(rnb);
324
 
325
			pos = new GPSPosition();
1565 - 326
			pos.setLongitude(((double)base64InputStream.readSignedDWord())/1E7);
327
			pos.setLatitude(((double)base64InputStream.readSignedDWord())/1E7);
328
			pos.setAltitude(((double)base64InputStream.readSignedDWord())/1E3);
1563 - 329
			pos.setStatus(base64InputStream.readByte());
330
			f.setHomePosition(pos);
331
 
332
			rnb = new GPSBearingAndRange();
1565 - 333
			rnb.setDistance(((double)base64InputStream.readWord())/10.0);
1563 - 334
			rnb.setBearing(base64InputStream.readSignedWord());
335
			f.setCurrentToHome(rnb);
336
 
337
			f.setWaypointIndex(base64InputStream.readByte());
338
			f.setWaypointCount(base64InputStream.readByte());
339
			f.setNumberOfSatellites(base64InputStream.readByte());
340
 
1565 - 341
			// This stunt is a metric unit conversion: The height was supposed (H&I) to be in integral 5cm steps.
342
			// However there is error factor in the measurement of 24% too much.
343
			// h[m] = h[int] * 0.05 / 1.24 = h[int]
344
			f._setHeightByPressure(((double)base64InputStream.readSignedWord()) * 0.05 / 1.24);
345
 
1568 - 346
			f.setVerticalVelocityByPressure(((double)base64InputStream.readSignedWord()) * 0.05 / 1.24); // clueless!
1563 - 347
			f.setFlightTime(base64InputStream.readWord());
348
			f.setBatteryVoltage(base64InputStream.readByte());
1568 - 349
			f.setGroundSpeed(((double)base64InputStream.readWord()) / 1E2);
1563 - 350
 
351
			f.setDirectionOfFlight(base64InputStream.readSignedWord());
352
			f.setCompassHeading(base64InputStream.readSignedWord());
353
 
354
			f.setPitchAngle(base64InputStream.readSignedByte());
355
			f.setRollAngle(base64InputStream.readSignedByte());
356
 
357
			f.setRcQuality(base64InputStream.readByte());
358
			f.setFcFlags(base64InputStream.readByte());
359
			f.setNcFlags(base64InputStream.readByte());
360
			f.setErrorCode(base64InputStream.readByte());
361
			f.setOperatingRadius(base64InputStream.readByte());
362
 
1568 - 363
			f.setVerticalVelocityByGPS(((double)base64InputStream.readSignedWord()) / 1E2);
1563 - 364
			f.setTargetLoiterTime(base64InputStream.readByte());
365
			f.setFcFlags2(base64InputStream.readByte());
1565 - 366
			f.setSetpointForAltitude(((double)base64InputStream.readSignedWord()) * 0.05 / 1.24);
1563 - 367
			f.setThrottle(base64InputStream.readByte());
368
			f.setCurrent(base64InputStream.readWord());
369
			f.setCapacityUsed(base64InputStream.readWord());
370
			result = f;
371
			break;
1690 - 372
		}
373
		case 'P': {
374
			ReadRCChannelsResponseFrame f = new ReadRCChannelsResponseFrame(address);
375
			int numberOfChannels = base64InputStream.readByte();
376
			f.setNumberOfChannels(numberOfChannels);
377
			f.setChannels(base64InputStream.readSignedWords(numberOfChannels));
378
			result = f;
379
			break;
380
		}
1563 - 381
		case 'S': {
1611 - 382
			WriteParamSetResponseFrame f = new WriteParamSetResponseFrame(address);
1563 - 383
			f.setParameterSetNumber(base64InputStream.readByte());
384
			result = f;
385
			break;
386
		}
387
		case 'T': {
388
			MotorTestResponseFrame f = new MotorTestResponseFrame(address);
389
			result = f;
390
			break;
391
		}
392
		/*
393
		 * We have a collision with the 'x' token: Also used for VariablesRequest.
394
		case 'x': {
395
			LoopbackTestResponseFrame f = new LoopbackTestResponseFrame(address);
396
			f.setByte(getDataInputStream().readByte());
397
			f.setWord(getDataInputStream().readWord());
398
			f.setChararray(getDataInputStream().readChars(8));
399
			result = f;
400
			break;
401
		}
402
   	    */
403
		case 'V': {
404
			VersionResponseFrame f = new VersionResponseFrame(address);
405
			f.setSWMajor(base64InputStream.readByte());
406
			f.setSWMinor(base64InputStream.readByte());
407
			f.setProtoMajor(base64InputStream.readByte());
408
			f.setProtoMinor(base64InputStream.readByte());
409
			f.setSWPatch(base64InputStream.readByte());
410
			f.setHardwareErrors(base64InputStream.readBytes(5));
411
			result = f;
412
			break;
413
		}
414
 
415
		// This is my own creation. The ID collides with the waypoint one of FC.
416
		case 'X': {
1688 - 417
			ReadVariablesResponseFrame f = new ReadVariablesResponseFrame(address);
1631 - 418
			f.setVariables(base64InputStream.readSignedWords(8));
1563 - 419
			result = f;
420
			break;
421
		}
422
		case 'w': {
423
			SetCompassHeadingResponseFrame f = new SetCompassHeadingResponseFrame(address);
424
			// do stuff.
425
			/*
426
			ToMk3Mag.Attitude[0] = (int16_t)((10 * angle[PITCH]) / GYRO_DEG_FACTOR_PITCHROLL); // approx. 0.1 deg
427
			ToMk3Mag.Attitude[1] = (int16_t)((10 * angle[ROLL]) / GYRO_DEG_FACTOR_PITCHROLL); // approx. 0.1 deg
428
			ToMk3Mag.UserParam[0] = dynamicParams.UserParams[0];
429
			ToMk3Mag.UserParam[1] = dynamicParams.UserParams[1];
430
			ToMk3Mag.CalState = compassCalState;
431
			*/
432
			// Waste 8 bytes to make CRC match.
433
			base64InputStream.readBytes(8);
434
			result = f;
435
			break;
436
		}
437
		case 'Q':
1611 - 438
			ReadParamSetResponseFrame f = new ReadParamSetResponseFrame(address);
1563 - 439
			f.setConfigurationSetNumber(base64InputStream.readByte());
440
			f.setConfigurationVersion(base64InputStream.readByte());
441
			int length = base64InputStream.readByte();
1601 - 442
			f.setConfigurationSetLength(length);
1563 - 443
			f.setData(base64InputStream.readBytes(length));
444
			result = f;
445
			break;
446
		default:
447
			int count = 0;
1573 - 448
			while(read() != '\r') {
1563 - 449
				count++;
450
			}
451
			System.err.println("Unknown frame " + (char)iid + " received from " + address);
452
			System.err.println("It appears to have " + (count-2) + " data bytes (encoded)");
453
			System.err.println("(" + (count-2) * 6/8 + " data bytes decoded)");
454
			result = null;
455
		}
456
 
457
		int receivedCRC = (read() - '=') << 6;
458
		receivedCRC += (read() - '=');
459
		crc %= 4096;
460
		if (receivedCRC != crc) {
461
			/// System.err.println("Expected CRC: " + crc + ", got CRC: " + receivedCRC);
462
			throw new IOException("CRC mismatch! Calculated crc: " + (int)crc + "; received check crc: " + receivedCRC + ", difference: " + Math.abs(crc - receivedCRC));
463
		}
464
		if (read() != '\r') {
465
			throw new IOException("CR at end of frame missing");
466
		}
467
 
468
		return result;
469
	}
470
}