esb/tr1001 interface
This commit is contained in:
parent
749c0aa639
commit
70dd3218f3
|
@ -0,0 +1,485 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: TR1001Radio.java,v 1.1 2008/02/07 14:55:35 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.cooja.mspmote.interfaces;
|
||||
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
import java.awt.event.*;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jdom.Element;
|
||||
|
||||
import se.sics.mspsim.core.*;
|
||||
import se.sics.cooja.*;
|
||||
import se.sics.cooja.interfaces.*;
|
||||
import se.sics.cooja.mspmote.ESBMote;
|
||||
|
||||
/**
|
||||
* TR1001 radio interface on ESB platform. Assumes driver specifics such as
|
||||
* preambles, synchbytes, GCR coding, CRC16.
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
@ClassDescription("TR1001 Radio")
|
||||
public class TR1001Radio extends Radio implements USARTListener, ByteRadio {
|
||||
private static Logger logger = Logger.getLogger(TR1001Radio.class);
|
||||
|
||||
/**
|
||||
* Minimum delay in CPU cycles between each byte fed to USART.
|
||||
*/
|
||||
private static final long CYCLES_BETWEEN_BYTES = 1200; /* ~19.200 bps */
|
||||
|
||||
private ESBMote mspMote;
|
||||
|
||||
private boolean radioOn = true;
|
||||
|
||||
private boolean transmitting = false;
|
||||
|
||||
private boolean isInterfered = false;
|
||||
|
||||
private RadioEvent lastEvent = RadioEvent.UNKNOWN;
|
||||
|
||||
private int lastEventTime = 0;
|
||||
|
||||
private USART radioUSART = null;
|
||||
|
||||
private byte[] packetToMote = null;
|
||||
|
||||
private byte[] packetFromMote = null;
|
||||
|
||||
/* Outgoing packet data buffer */
|
||||
private byte[] outgoingData = new byte[1024]; // TODO Decrease max size
|
||||
|
||||
private int outgoingDataLength = 0;
|
||||
|
||||
private int ticksSinceLastSend = -1;
|
||||
|
||||
/* Incoming byte-to-packet data buffer */
|
||||
private Vector<Byte> bufferedBytes = new Vector<Byte>();
|
||||
|
||||
private Vector<Long> bufferedByteDelays = new Vector<Long>();
|
||||
|
||||
/* Outgoing byte data buffer */
|
||||
private byte byteFromMote = -1;
|
||||
|
||||
private long transmissionStartCycles = -1;
|
||||
|
||||
private long byteFromMoteDelay = -1;
|
||||
|
||||
/* Incoming byte data buffer */
|
||||
private byte byteToMote = -1;
|
||||
|
||||
private byte lastDeliveredByte = -1;
|
||||
|
||||
private long lastDeliveredByteTimestamp = -1;
|
||||
|
||||
private long lastDeliveredByteDelay = -1;
|
||||
|
||||
private TR1001RadioPacketConverter tr1001PacketConverter = null;
|
||||
|
||||
/**
|
||||
* Creates an interface to the TR1001 radio at mote.
|
||||
*
|
||||
* @param mote
|
||||
* Radio's mote.
|
||||
* @see Mote
|
||||
* @see se.sics.cooja.MoteInterfaceHandler
|
||||
*/
|
||||
public TR1001Radio(ESBMote mote) {
|
||||
mspMote = mote;
|
||||
|
||||
/* Start listening to CPU's USART */
|
||||
IOUnit usart = mote.getCPU().getIOUnit("USART 0");
|
||||
if (usart instanceof USART) {
|
||||
radioUSART = (USART) usart;
|
||||
radioUSART.setUSARTListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
/* Packet radio support */
|
||||
public byte[] getLastPacketTransmitted() {
|
||||
return packetFromMote;
|
||||
}
|
||||
|
||||
public byte[] getLastPacketReceived() {
|
||||
return packetToMote;
|
||||
}
|
||||
|
||||
public void setReceivedPacket(byte[] p) {
|
||||
packetToMote = p;
|
||||
if (packetToMote == null || packetToMote.length == 0) {
|
||||
logger.fatal("Received null packet");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInterfered) {
|
||||
logger.fatal("Received packet when interfered");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Convert to TR1001 packet data */
|
||||
byte[] tr1001Frame = TR1001RadioPacketConverter.fromCoojaToTR1001(p);
|
||||
|
||||
/* Feed to the CPU "slowly" */
|
||||
for (byte element : tr1001Frame) {
|
||||
receiveByte(element, CYCLES_BETWEEN_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
/* Byte radio support */
|
||||
public byte getLastByteTransmitted() {
|
||||
return byteFromMote;
|
||||
}
|
||||
|
||||
public long getLastByteTransmittedDelay() {
|
||||
return byteFromMoteDelay;
|
||||
}
|
||||
|
||||
public byte getLastByteReceived() {
|
||||
return byteToMote;
|
||||
}
|
||||
|
||||
public void receiveByte(byte b, long delay) {
|
||||
byteToMote = b;
|
||||
bufferedBytes.add(b);
|
||||
bufferedByteDelays.add(delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if undelivered bytes exist.
|
||||
*/
|
||||
public boolean hasPendingBytes() {
|
||||
return bufferedBytes.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* If non-delivered bytes exist, tries to deliver one byte to the CPU by
|
||||
* checking USART receive flag.
|
||||
*
|
||||
* @param cycles
|
||||
* Current CPU cycles
|
||||
*/
|
||||
public void tryDeliverNextByte(long cycles) {
|
||||
// Check that pending bytes exist
|
||||
if (!hasPendingBytes()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if time to deliver byte
|
||||
long nextByteDelay = bufferedByteDelays.firstElement();
|
||||
if (cycles - lastDeliveredByteDelay < nextByteDelay) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastDeliveredByte = bufferedBytes.firstElement();
|
||||
|
||||
bufferedBytes.remove(0);
|
||||
bufferedByteDelays.remove(0);
|
||||
|
||||
if (radioUSART.isReceiveFlagCleared()) {
|
||||
//logger.info(nextByteDelay + " < "
|
||||
// + (cycles - receptionStartedCycles)
|
||||
// + ":\tDelivering 0x" + Utils.hex8(lastDeliveredByte) + " (TODO="
|
||||
// + bufferedBytes.size() + ")");
|
||||
radioUSART.byteReceived(lastDeliveredByte);
|
||||
} else {
|
||||
/*logger.fatal(nextByteDelay + " < "
|
||||
+ (cycles - receptionStartedCycles)
|
||||
+ ":\tDROPPING 0x" + Utils.hex8(lastDeliveredByte) + " (TODO="
|
||||
+ bufferedBytes.size() + ")");*/
|
||||
}
|
||||
lastDeliveredByteDelay = cycles;
|
||||
|
||||
// /* TODO BUG: Resends last byte, interrupt lost somewhere? */
|
||||
// else if (cycles > lastDeliveredByteTimestamp + CYCLES_BETWEEN_BYTES) {
|
||||
// logger.warn("0x" + Utils.hex16((int) cycles) + ":\tRedelivering 0x"
|
||||
// + Utils.hex8(lastDeliveredByte) + " (TODO=" + bufferedBytes.size()
|
||||
// + ")");
|
||||
// radioUSART.byteReceived(lastDeliveredByte);
|
||||
// lastDeliveredByteTimestamp = cycles;
|
||||
// }
|
||||
}
|
||||
|
||||
/* USART listener support */
|
||||
public void dataReceived(USART source, int data) {
|
||||
if (outgoingDataLength == 0 && !isTransmitting()) {
|
||||
/* New transmission discovered */
|
||||
//logger.debug("----- NEW MSP TRANSMISSION DETECTED -----");
|
||||
tr1001PacketConverter = new TR1001RadioPacketConverter();
|
||||
|
||||
transmitting = true;
|
||||
lastEventTime = mspMote.getSimulation().getSimulationTime();
|
||||
lastEvent = RadioEvent.TRANSMISSION_STARTED;
|
||||
transmissionStartCycles = mspMote.getCPU().cycles;
|
||||
lastDeliveredByteTimestamp = transmissionStartCycles;
|
||||
this.setChanged();
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
// Remember recent radio activity
|
||||
ticksSinceLastSend = 0;
|
||||
|
||||
if (outgoingDataLength >= outgoingData.length) {
|
||||
logger.fatal("Ignoring byte due to buffer overflow");
|
||||
return;
|
||||
}
|
||||
|
||||
// Store packet data
|
||||
outgoingData[outgoingDataLength++] = (byte) data;
|
||||
|
||||
// Deliver byte to radio medium
|
||||
lastEvent = RadioEvent.BYTE_TRANSMITTED;
|
||||
byteFromMote = (byte) data;
|
||||
byteFromMoteDelay = mspMote.getCPU().cycles - lastDeliveredByteTimestamp;
|
||||
lastDeliveredByteTimestamp = mspMote.getCPU().cycles;
|
||||
if (byteFromMoteDelay < 0) {
|
||||
byteFromMoteDelay = 0;
|
||||
}
|
||||
|
||||
this.setChanged();
|
||||
this.notifyObservers();
|
||||
|
||||
// Feed to application level immediately
|
||||
boolean finished = tr1001PacketConverter.fromTR1001ToCoojaAccumulated(byteFromMote);
|
||||
if (finished) {
|
||||
/* Transmission finished - deliver packet immediately */
|
||||
if (tr1001PacketConverter.accumulatedConversionIsOk()) {
|
||||
packetFromMote = tr1001PacketConverter.getAccumulatedConvertedData();
|
||||
} else {
|
||||
packetFromMote = new byte[0];
|
||||
}
|
||||
|
||||
/* Notify observers of new prepared packet */
|
||||
/*logger.debug("----- MSP DELIVERED PACKET -----");*/
|
||||
lastEvent = RadioEvent.PACKET_TRANSMITTED;
|
||||
this.setChanged();
|
||||
this.notifyObservers();
|
||||
|
||||
// Reset counters and wait for next packet
|
||||
outgoingDataLength = 0;
|
||||
ticksSinceLastSend = -1;
|
||||
|
||||
// Signal we are done transmitting
|
||||
transmitting = false;
|
||||
lastEvent = RadioEvent.TRANSMISSION_FINISHED;
|
||||
this.setChanged();
|
||||
this.notifyObservers();
|
||||
|
||||
/*logger.debug("----- MSP TRANSMISSION ENDED -----");*/
|
||||
}
|
||||
}
|
||||
|
||||
/* General radio support */
|
||||
public boolean isTransmitting() {
|
||||
return transmitting;
|
||||
}
|
||||
|
||||
public boolean isReceiving() {
|
||||
return hasPendingBytes();
|
||||
}
|
||||
|
||||
public boolean isInterfered() {
|
||||
return isInterfered;
|
||||
}
|
||||
|
||||
public int getChannel() {
|
||||
// TODO Implement support for channels
|
||||
return 1;
|
||||
}
|
||||
|
||||
public void signalReceptionStart() {
|
||||
lastEvent = RadioEvent.RECEPTION_STARTED;
|
||||
/*receptionStartedCycles = mspMote.getCPU().cycles;*/
|
||||
this.setChanged();
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
public void signalReceptionEnd() {
|
||||
// TODO Should be done according to serial port instead
|
||||
// TODO Compare times with OS abstraction level
|
||||
if (isInterfered()) {
|
||||
isInterfered = false;
|
||||
return;
|
||||
}
|
||||
lastEvent = RadioEvent.RECEPTION_FINISHED;
|
||||
this.setChanged();
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
public RadioEvent getLastEvent() {
|
||||
return lastEvent;
|
||||
}
|
||||
|
||||
public void interfereAnyReception() {
|
||||
if (!isInterfered()) {
|
||||
isInterfered = true;
|
||||
|
||||
bufferedBytes.clear();
|
||||
bufferedByteDelays.clear();
|
||||
|
||||
lastEvent = RadioEvent.RECEPTION_INTERFERED;
|
||||
lastEventTime = mspMote.getSimulation().getSimulationTime();
|
||||
this.setChanged();
|
||||
this.notifyObservers();
|
||||
}
|
||||
}
|
||||
|
||||
public double getCurrentOutputPower() {
|
||||
// TODO Implement method
|
||||
return 1.5;
|
||||
}
|
||||
|
||||
public int getCurrentOutputPowerIndicator() {
|
||||
// TODO Implement output power indicator
|
||||
return 100;
|
||||
}
|
||||
|
||||
public double getCurrentSignalStrength() {
|
||||
// TODO Implement signal strength
|
||||
return 100;
|
||||
}
|
||||
|
||||
public void setCurrentSignalStrength(double signalStrength) {
|
||||
// TODO Implement signal strength
|
||||
// logger.warn("Not implemented");
|
||||
}
|
||||
|
||||
public Position getPosition() {
|
||||
return mspMote.getInterfaces().getPosition();
|
||||
}
|
||||
|
||||
public void doActionsBeforeTick() {
|
||||
}
|
||||
|
||||
public void doActionsAfterTick() {
|
||||
// Detect transmission end due to inactivity
|
||||
if (isTransmitting() && ticksSinceLastSend > 4) {
|
||||
/* Dropping packet due to inactivity */
|
||||
packetFromMote = new byte[0];
|
||||
|
||||
/* Notify observers of new empty packet */
|
||||
logger.warn("----- DELIVERED MSP NULL PACKET -----");
|
||||
lastEvent = RadioEvent.PACKET_TRANSMITTED;
|
||||
this.setChanged();
|
||||
this.notifyObservers();
|
||||
|
||||
// Reset counters and wait for next packet
|
||||
outgoingDataLength = 0;
|
||||
ticksSinceLastSend = -1;
|
||||
|
||||
// Signal we are done transmitting
|
||||
transmitting = false;
|
||||
lastEvent = RadioEvent.TRANSMISSION_FINISHED;
|
||||
this.setChanged();
|
||||
this.notifyObservers();
|
||||
|
||||
//logger.debug("----- NULL TRANSMISSION ENDED -----");
|
||||
} else if (isTransmitting() && ticksSinceLastSend >= 0) {
|
||||
// Increase counter to detect when transmission ends
|
||||
ticksSinceLastSend++;
|
||||
}
|
||||
}
|
||||
|
||||
public JPanel getInterfaceVisualizer() {
|
||||
// Location
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
||||
|
||||
final JLabel statusLabel = new JLabel("");
|
||||
final JLabel lastEventLabel = new JLabel("");
|
||||
final JLabel ssLabel = new JLabel("");
|
||||
final JButton updateButton = new JButton("Update SS");
|
||||
|
||||
panel.add(statusLabel);
|
||||
panel.add(lastEventLabel);
|
||||
panel.add(ssLabel);
|
||||
panel.add(updateButton);
|
||||
|
||||
updateButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
ssLabel.setText("Signal strength (not auto-updated): "
|
||||
+ getCurrentSignalStrength() + " dBm");
|
||||
}
|
||||
});
|
||||
|
||||
Observer observer;
|
||||
this.addObserver(observer = new Observer() {
|
||||
public void update(Observable obs, Object obj) {
|
||||
if (isTransmitting()) {
|
||||
statusLabel.setText("Transmitting packet now!");
|
||||
} else if (isReceiving()) {
|
||||
statusLabel.setText("Receiving packet now!");
|
||||
} else if (radioOn) {
|
||||
statusLabel.setText("Listening...");
|
||||
} else {
|
||||
statusLabel.setText("HW turned off");
|
||||
}
|
||||
|
||||
lastEventLabel.setText("Last event (time=" + lastEventTime + "): "
|
||||
+ lastEvent);
|
||||
ssLabel.setText("Signal strength (not auto-updated): "
|
||||
+ getCurrentSignalStrength() + " dBm");
|
||||
}
|
||||
});
|
||||
|
||||
observer.update(null, null);
|
||||
|
||||
// Saving observer reference for releaseInterfaceVisualizer
|
||||
panel.putClientProperty("intf_obs", observer);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
public void releaseInterfaceVisualizer(JPanel panel) {
|
||||
Observer observer = (Observer) panel.getClientProperty("intf_obs");
|
||||
if (observer == null) {
|
||||
logger.fatal("Error when releasing panel, observer is null");
|
||||
return;
|
||||
}
|
||||
|
||||
this.deleteObserver(observer);
|
||||
}
|
||||
|
||||
public double energyConsumptionPerTick() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Collection<Element> getConfigXML() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setConfigXML(Collection<Element> configXML, boolean visAvailable) {
|
||||
}
|
||||
|
||||
public Mote getMote() {
|
||||
return mspMote;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: TR1001RadioPacketConverter.java,v 1.1 2008/02/07 14:55:35 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.cooja.mspmote.interfaces;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Converts radio packets between TR1001/ESB and COOJA.
|
||||
* Some functionality, such as GCR coding, is ESB TR1001 driver specific.
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
public class TR1001RadioPacketConverter {
|
||||
private static Logger logger = Logger.getLogger(TR1001RadioPacketConverter.class);
|
||||
|
||||
private static GCRCoder gcrCoder = new GCRCoder();
|
||||
|
||||
/* TR1001 packet: PREAMBLE(20) _ SYNCH(1+4+1) _ GCR(ESBDATA(?)) _ TRAIL(4) */
|
||||
final static int TR1001_PREAMBLE_LENGTH = 20;
|
||||
|
||||
final static int TR1001_NR_SYNCHBYTES = 4;
|
||||
|
||||
final static byte TR1001_SYNCH1 = 0x3c;
|
||||
|
||||
final static byte TR1001_SYNCH2 = 0x03;
|
||||
|
||||
final static int TR1001_HEADER_LENGTH = TR1001_PREAMBLE_LENGTH + 1
|
||||
+ TR1001_NR_SYNCHBYTES + 1;
|
||||
|
||||
final static int TR1001_FOOTER_LENGTH = 4;
|
||||
|
||||
/* "ESB" packet: LENGTH(2) _ APPDATA(?) _ CRC(2) */
|
||||
final static int ESB_HEADER_LENGTH = 2;
|
||||
|
||||
final static int ESB_FOOTER_LENGTH = 2;
|
||||
|
||||
|
||||
private enum AccumulatedConversionState {
|
||||
TR1001_PREAMBLE, TR1001_SYNCH, ESB_LEN1, ESB_LEN2, ESB_DATA, ESB_CRC1, ESB_CRC2, ESB_POST,
|
||||
}
|
||||
|
||||
private AccumulatedConversionState accumulatedConversionState = AccumulatedConversionState.TR1001_PREAMBLE;
|
||||
|
||||
private boolean accumulatedConversionOK = true;
|
||||
|
||||
private int[] accumulatedConversionDataLengthArray = new int[2];
|
||||
|
||||
private int[] accumulatedConversionDataArray = null;
|
||||
|
||||
private int[] accumulatedConversionCRCArray = new int[2];
|
||||
|
||||
private int accumulatedConversionDataCounter = 0;
|
||||
|
||||
private int accumulatedConversionDataLength = 0;
|
||||
|
||||
private int accumulatedConversionFooterLength = 0;
|
||||
|
||||
private GCRCoder AccumulatedConversionGCRCoder = new GCRCoder();
|
||||
|
||||
/**
|
||||
* Converts radio packet data from COOJA to TR1001. This
|
||||
* implementation is ESB platform and TR1001 driver specific.
|
||||
*
|
||||
* It consists of (in sequential order):
|
||||
* - Adding 2-byte length header
|
||||
* - Calculating and adding 2-byte CRC16 footer
|
||||
* - GCR encoding data
|
||||
* - Add TR1001 header: preamble and synch bytes
|
||||
* - Add TR1001 footer: 4 trail bytes
|
||||
*
|
||||
* The returned array typically needs to be feeded to the emulated "slowly",
|
||||
* i.e. one by one and using interrupts.
|
||||
*
|
||||
* @param coojaPacket
|
||||
* COOJA radio packet
|
||||
* @return TR1001 radio packet
|
||||
*/
|
||||
public static byte[] fromCoojaToTR1001(byte[] coojaPacket) {
|
||||
|
||||
/* Create "ESB" packet: add length header and CRC16 footer */
|
||||
byte[] esbPacket = addLengthAndCRC(coojaPacket);
|
||||
|
||||
/* GCR encode radio frame */
|
||||
byte[] encodedData = gcrCoder.gcrEncode(esbPacket, esbPacket.length);
|
||||
|
||||
/* Create TR1001 header (preamble and synchbytes) */
|
||||
byte[] tr1001Frame = new byte[TR1001_HEADER_LENGTH + encodedData.length
|
||||
+ TR1001_FOOTER_LENGTH];
|
||||
for (int i = 0; i < TR1001_PREAMBLE_LENGTH; i++) {
|
||||
tr1001Frame[i] = (byte) 0xaa;
|
||||
}
|
||||
tr1001Frame[TR1001_PREAMBLE_LENGTH] = (byte) 0xff;
|
||||
for (int i = 0; i < TR1001_NR_SYNCHBYTES; i++) {
|
||||
tr1001Frame[TR1001_PREAMBLE_LENGTH + 1 + i] = TR1001_SYNCH1;
|
||||
}
|
||||
tr1001Frame[TR1001_PREAMBLE_LENGTH + 1 + TR1001_NR_SYNCHBYTES] = TR1001_SYNCH2;
|
||||
|
||||
/* Add encoded data */
|
||||
System.arraycopy(encodedData, 0, tr1001Frame, TR1001_HEADER_LENGTH,
|
||||
encodedData.length);
|
||||
|
||||
/* Add TR1001 footer (trail bytes) */
|
||||
tr1001Frame[TR1001_HEADER_LENGTH + encodedData.length] = (byte) 0x33;
|
||||
tr1001Frame[TR1001_HEADER_LENGTH + encodedData.length + 1] = (byte) 0xcc;
|
||||
tr1001Frame[TR1001_HEADER_LENGTH + encodedData.length + 2] = (byte) 0x33;
|
||||
tr1001Frame[TR1001_HEADER_LENGTH + encodedData.length + 3] = (byte) 0xcc;
|
||||
|
||||
return tr1001Frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts radio packet data from TR1001 to COOJA. This
|
||||
* implementation is ESB platform and TR1001 driver specific.
|
||||
*
|
||||
* It consists of (in sequential order):
|
||||
* - Removing TR1001 header and footer
|
||||
* - GCR decode data
|
||||
* - Read length header
|
||||
* - Remove both length header and CRC footer
|
||||
*
|
||||
* @param tr1001Data
|
||||
* TR1001 packet data
|
||||
* @param tr1001DataLength
|
||||
* TR1001 packet data length
|
||||
* @return COOJA radio packet
|
||||
*/
|
||||
public static byte[] fromTR1001ToCooja(byte[] tr1001Data,
|
||||
int tr1001DataLength) {
|
||||
/* Remove TR1001 specifics: preamble, synch and trail bytes */
|
||||
System.arraycopy(tr1001Data, TR1001_HEADER_LENGTH, tr1001Data, 0,
|
||||
tr1001DataLength - TR1001_HEADER_LENGTH - TR1001_FOOTER_LENGTH);
|
||||
tr1001DataLength = tr1001DataLength - TR1001_HEADER_LENGTH
|
||||
- TR1001_FOOTER_LENGTH;
|
||||
|
||||
/* GCR decode */
|
||||
byte[] decodedData = gcrCoder.gcrDecode(tr1001Data, tr1001DataLength);
|
||||
|
||||
if (decodedData != null) {
|
||||
/* Decoding succeded, fetch length from the two first bytes */
|
||||
int dataLength = ((decodedData[0] & 0xff) << 8) + (decodedData[1] & 0xff);
|
||||
|
||||
if (dataLength + ESB_HEADER_LENGTH + ESB_HEADER_LENGTH > decodedData.length) {
|
||||
logger.fatal("Decoded data is smaller than specified data length: "
|
||||
+ (dataLength + ESB_HEADER_LENGTH + ESB_HEADER_LENGTH) + " vs "
|
||||
+ decodedData.length);
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
/* Remove ESB header and CRC footer */
|
||||
byte[] packetData = new byte[dataLength];
|
||||
System.arraycopy(decodedData, ESB_HEADER_LENGTH, packetData, 0,
|
||||
dataLength);
|
||||
|
||||
return packetData;
|
||||
}
|
||||
|
||||
logger
|
||||
.fatal("Error when converting emulated to application level, returning empty: "
|
||||
+ tr1001DataLength);
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds length header and CRC16 footer to given data.
|
||||
*
|
||||
* @param packetData
|
||||
* Packet data
|
||||
* @return Packet data with length header and CRC16 footer
|
||||
*/
|
||||
private static byte[] addLengthAndCRC(byte[] packetData) {
|
||||
short accumulatedCRC = (short) 0xffff;
|
||||
byte[] radioFrame = new byte[ESB_HEADER_LENGTH + packetData.length
|
||||
+ ESB_FOOTER_LENGTH];
|
||||
|
||||
/* Add length */
|
||||
radioFrame[0] = (byte) ((packetData.length >> 8) & 0xff);
|
||||
accumulatedCRC = CRCCoder.crc16Add(radioFrame[0], accumulatedCRC);
|
||||
radioFrame[1] = (byte) (packetData.length & 0xff);
|
||||
accumulatedCRC = CRCCoder.crc16Add(radioFrame[1], accumulatedCRC);
|
||||
|
||||
/* Add data */
|
||||
System.arraycopy(packetData, 0, radioFrame, ESB_HEADER_LENGTH,
|
||||
packetData.length);
|
||||
|
||||
/* Calculate CRC */
|
||||
for (byte element : packetData) {
|
||||
accumulatedCRC = CRCCoder.crc16Add(element, accumulatedCRC);
|
||||
}
|
||||
|
||||
/* Add CRC */
|
||||
radioFrame[ESB_HEADER_LENGTH + packetData.length] = (byte) ((accumulatedCRC >> 8) & 0xff);
|
||||
radioFrame[ESB_HEADER_LENGTH + packetData.length + 1] = (byte) (accumulatedCRC & 0xff);
|
||||
return radioFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds another another byte to the accumulated conversion from TR1001
|
||||
* to Cooja.
|
||||
*
|
||||
* @see #accumulatedConversionIsOk()
|
||||
* @param b
|
||||
* Byte
|
||||
* @return True if conversion finished (either successful of failed).
|
||||
*/
|
||||
public boolean fromTR1001ToCoojaAccumulated(byte b) {
|
||||
if (accumulatedConversionState == AccumulatedConversionState.TR1001_PREAMBLE) {
|
||||
if (b == (byte) 0xaa || b == (byte) 0xff) {
|
||||
return false;
|
||||
} else if (b == (byte) 0x3c) {
|
||||
accumulatedConversionState = AccumulatedConversionState.TR1001_SYNCH;
|
||||
return false;
|
||||
} else {
|
||||
accumulatedConversionOK = false;
|
||||
return true;
|
||||
}
|
||||
} else if (accumulatedConversionState == AccumulatedConversionState.TR1001_SYNCH) {
|
||||
if (b == TR1001_SYNCH1) {
|
||||
return false;
|
||||
} else if (b == TR1001_SYNCH2) {
|
||||
accumulatedConversionState = AccumulatedConversionState.ESB_LEN1;
|
||||
AccumulatedConversionGCRCoder.gcr_init();
|
||||
return false;
|
||||
} else {
|
||||
accumulatedConversionOK = false;
|
||||
return true;
|
||||
}
|
||||
} else if (accumulatedConversionState == AccumulatedConversionState.ESB_LEN1) {
|
||||
AccumulatedConversionGCRCoder.gcr_decode(0xff & b);
|
||||
if (AccumulatedConversionGCRCoder.gcr_get_decoded(accumulatedConversionDataLengthArray, 0)) {
|
||||
accumulatedConversionState = AccumulatedConversionState.ESB_LEN2;
|
||||
}
|
||||
return false;
|
||||
} else if (accumulatedConversionState == AccumulatedConversionState.ESB_LEN2) {
|
||||
AccumulatedConversionGCRCoder.gcr_decode(0xff & b);
|
||||
if (AccumulatedConversionGCRCoder.gcr_get_decoded(accumulatedConversionDataLengthArray, 1)) {
|
||||
accumulatedConversionState = AccumulatedConversionState.ESB_DATA;
|
||||
accumulatedConversionDataLength = (accumulatedConversionDataLengthArray[0] & 0xff) << 8;
|
||||
accumulatedConversionDataLength = (accumulatedConversionDataLengthArray[1] & 0xff);
|
||||
accumulatedConversionDataArray = new int[accumulatedConversionDataLength];
|
||||
}
|
||||
return false;
|
||||
} else if (accumulatedConversionState == AccumulatedConversionState.ESB_DATA) {
|
||||
AccumulatedConversionGCRCoder.gcr_decode(0xff & b);
|
||||
if (AccumulatedConversionGCRCoder.gcr_get_decoded(accumulatedConversionDataArray, accumulatedConversionDataCounter)) {
|
||||
accumulatedConversionDataCounter++;
|
||||
if (accumulatedConversionDataCounter >= accumulatedConversionDataArray.length) {
|
||||
accumulatedConversionState = AccumulatedConversionState.ESB_CRC1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (accumulatedConversionState == AccumulatedConversionState.ESB_CRC1) {
|
||||
AccumulatedConversionGCRCoder.gcr_decode(0xff & b);
|
||||
if (AccumulatedConversionGCRCoder.gcr_get_decoded(accumulatedConversionCRCArray, 0)) {
|
||||
accumulatedConversionState = AccumulatedConversionState.ESB_CRC2;
|
||||
}
|
||||
return false;
|
||||
} else if (accumulatedConversionState == AccumulatedConversionState.ESB_CRC2) {
|
||||
AccumulatedConversionGCRCoder.gcr_decode(0xff & b);
|
||||
if (AccumulatedConversionGCRCoder.gcr_get_decoded(accumulatedConversionCRCArray, 1)) {
|
||||
accumulatedConversionState = AccumulatedConversionState.ESB_POST;
|
||||
}
|
||||
return false;
|
||||
} else if (accumulatedConversionState == AccumulatedConversionState.ESB_POST) {
|
||||
accumulatedConversionFooterLength++;
|
||||
return accumulatedConversionFooterLength >= 4;
|
||||
} else {
|
||||
accumulatedConversionOK = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Converted data (application level)
|
||||
*/
|
||||
public byte[] getAccumulatedConvertedData() {
|
||||
byte[] dataArrayByte = new byte[accumulatedConversionDataArray.length];
|
||||
for (int i = 0; i < accumulatedConversionDataArray.length; i++) {
|
||||
dataArrayByte[i] = (byte) accumulatedConversionDataArray[i];
|
||||
}
|
||||
return dataArrayByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns status from accumulated conversion.
|
||||
*
|
||||
* @return True if ok, false if error occurred.
|
||||
*/
|
||||
public boolean accumulatedConversionIsOk() {
|
||||
return accumulatedConversionOK;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue