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