package interference;
import java.util.Random;
public class LoRaTransceiver {
public static double getNumberOfReceivedErrorBits(String inputMsg, int spreadingFactor) {
System.out.println(spreadingFactor);
// The number of received error bits to return
double errBits = 0;
// ------------------------------------------------------
// LoRa Modem Settings
int CR = 0; // [0 1 2 3 4] Coding Rate
double BW = 125e3; // [7.8khz - 500khz] % BandWidth
double CodingRate = 4 / (CR + 4); // Coding Rate
// ------------------------------------------------------
// Parameters for Chirp Spread Spectrum Modulation
int A = 1; // Signal amplitude
double fMin = 0; // Minimum frequency
double fMax = BW; // Maximum frequency
double fs = 5 * fMax; // Sampling frequency 2*fmax
double Tchirp = 1e-3; // Time for one Chirp
double inter = 1 / fs; // inter = 1/fs = 1.6000e-006
double[] timeInter = new double[626];
timeInter[0] = fMin;
for (int i = 1; i < 626; i++) {
timeInter[i] = timeInter[i - 1] + inter; // The time interval of a
// signal of a duration
// of a symbol time
}
// ------------------------------------------------------
// generating Chirp signal
double b = (fMax - fMin) / Tchirp;
double[] f = new double[626];
double[] chirp = new double[626];
for (int i = 0; i < 626; i++) {
f[i] = timeInter[i] * b + fMin;
chirp[i] = A * Math.cos(2 * Math.PI * f[i] * timeInter[i]); // generating
// Chirp
// Signal
}
// ------------------------------------------------------
// LoRa Transmitter
// ------------------------------------------------------
int symbolSize = 4;
byte[] binaryData = getStringToBinary(inputMsg);
byte[] binarySource = new byte[64];
if (binaryData.length >= 512)
System.out.print("Too Long Message");
else {
for (int i = 0; i < 64; i++) {
if (i < binaryData.length)
binarySource[i] = binaryData[i];
else
binarySource[i] = 0;
}
int tx_b = binarySource.length;
// ------------------------------------------------------
// FEC EnCoding
int CodInB = 0; // codInB = coder input bits
int CodOutB = 0; // codOutB = coder output bits
if (CR == 0) {
CodInB = 4;
CodOutB = 4;
} else if (CR == 1) {
CodInB = 4;
CodOutB = 5;
} else if (CR == 2) {
CodInB = 2;
CodOutB = 3;
} else if (CR == 3) {
CodInB = 4;
CodOutB = 7;
} else if (CR == 4) {
CodInB = 1;
CodOutB = 2;
} else {
System.out.println("CR value should be b/w 1-4");
}
byte[] codedSource = null;
codedSource = encoder(CR, binarySource, CodInB, CodOutB);
// Performing NRZ
int codedSourceNRZ[] = new int[tx_b];
for (int i = 0; i < tx_b; i++) {
codedSourceNRZ[i] = 2 * codedSource[i] - 1;
}
// ------------------------------------------------------
// InterLeaving
int codedSourceNRZmat[][] = new int[tx_b / symbolSize][CodOutB];
for (int i = 0; i < tx_b / symbolSize; i++) {
for (int j = 0; j < CodOutB; j++) {
codedSourceNRZmat[i][j] = codedSourceNRZ[(i * CodOutB) + j]; // first
// converting
// coded
// data
// into
// matrix
}
}
int[][] interleavedSource = new int[tx_b / symbolSize][CodOutB];
for (int i = 0; i < tx_b / symbolSize; i++) {
for (int j = 0; j < CodOutB; j++) {
interleavedSource[i][j] = codedSourceNRZmat[(i + j) % (tx_b / symbolSize)][j]; // Interleaving
}
}
int[] interleavedSourceSerial = new int[tx_b];
int ss = 0;
for (int i = 0; i < tx_b / symbolSize; i++) {
for (int j = 0; j < CodOutB; j++) {
interleavedSourceSerial[ss++] = interleavedSource[i][j]; // Again
// converting
// into
// serial
}
}
// ------------------------------------------------------
// Spreading
int codelength = (int) Math.pow(2, spreadingFactor); // Length of generated code
// bits
double[] spreadingCode = getcode(codelength);
int[] spreadedSeq = new int[tx_b * codelength];
for (int i = 0; i < tx_b; i++) {
for (int j = 0; j < codelength; j++) {
spreadedSeq[i * codelength + j] = (int) (interleavedSourceSerial[i] * spreadingCode[j]);
}
}
// ------------------------------------------------------
// Chirp Modulation
int rows = spreadedSeq.length;
int cols = chirp.length;
double[][] tx_modulated = new double[rows][cols];
double[] txSignal = new double[rows * cols];
for (int i = 0; i < spreadedSeq.length; i++) {
for (int j = 0; j < chirp.length; j++) {
tx_modulated[i][j] = spreadedSeq[i] * chirp[j];
txSignal[i * cols + j] = tx_modulated[i][j];
}
}
// ------------------------------------------------------
// AWGN Channel
// ------------------------------------------------------
double[] rxSignal = AWGNchannel(spreadingFactor, CodingRate, spreadingCode, txSignal);
// ------------------------------------------------------
// LoRa Receiver
// ------------------------------------------------------
// De-Modulation
double x;
double[] rxDemodulated = new double[rxSignal.length / chirp.length];
for (int i = 0; i < rxSignal.length / chirp.length; i++) {
x = 0;
for (int j = 0; j < chirp.length; j++) {
x = x + rxSignal[i * chirp.length + j] * chirp[j];
}
rxDemodulated[i] = x;
}
// De-Spreading
double[] rxDespread = new double[rxDemodulated.length / codelength];
for (int i = 0; i < rxDemodulated.length / codelength; i++) {
x = 0;
for (int j = 0; j < codelength; j++) {
x = x + spreadingCode[j] * rxDemodulated[i * codelength + j];
}
rxDespread[i] = x / codelength;
}
// De-Interleaving
double[][] rxDespreadMat = new double[rxDespread.length / symbolSize][symbolSize];
for (int i = 0; i < rxDespread.length / symbolSize; i++) {
for (int j = 0; j < symbolSize; j++) {
rxDespreadMat[i][j] = rxDespread[(i * symbolSize) + j]; // first
// converting
// de-spread
// data
// into
// matrix
}
}
double[][] rxDeInterleave = new double[rxDespread.length / symbolSize][symbolSize];
double s = rxDespread.length / symbolSize;
for (int j = 0; j < symbolSize; j++) {
for (int i = 0; i < rxDespread.length / symbolSize; i++) {
if (i < j) {
s = (rxDespread.length / symbolSize - j) + i;
} else {
s = i - j;
}
rxDeInterleave[i][j] = rxDespreadMat[(int) s][j];
}
}
// De-Whitening
double[][] rxDeNRZ = new double[rxDespread.length / symbolSize][symbolSize];
for (int i = 0; i < rxDespread.length / symbolSize; i++) {
for (int j = 0; j < symbolSize; j++) {
if (rxDeInterleave[i][j] > 0)
rxDeNRZ[i][j] = 1;
else
rxDeNRZ[i][j] = 0;
}
}
// parallel to serial conversion
double[] receivedData = new double[rxDespread.length / symbolSize * symbolSize];
int ss1 = 0;
for (int i = 0; i < rxDespread.length / symbolSize; i++) {
for (int j = 0; j < symbolSize; j++) {
receivedData[ss1++] = rxDeNRZ[i][j];
}
}
// Calculating if Packet is Received Correctly
byte[] cb = new byte[receivedData.length];
for (int i = 0; i < receivedData.length; i++) {
cb[i] = binarySource[i];
errBits += (cb[i] == receivedData[i]) ? 0 : 1;
}
}
// errBits = Error Bits in the received data
// if errBits == 0 -> Packet Received Correctly
// otherwise -> Packet Not Received Correctly
return errBits ;
}
// ------------------------------------------------------
// Other Functions
// ------------------------------------------------------
// Encoder
public static byte[] encoder(int CR, byte[] binarySource, int CodInB, int CodOutB) {
byte[] reg1 = { 0, 0, 0, 0, 0 };
byte[] reg2 = { 0, 0, 0, 0 };
byte[] reg3 = { 0, 0, 0, 0, 0 };
byte[] reg4 = { 0, 0, 0, 0 };
byte[][] codedSource = null;
byte[] codedSourceOut = null;
int nSymbols = binarySource.length / CodInB;
codedSource = new byte[nSymbols][CodOutB];
codedSourceOut = new byte[nSymbols * CodOutB];
switch (CR) {
// ------------------------------------------------------
case 0:
codedSourceOut = binarySource;
break;
case 1: // coding rate 4/5
for (int i = 0; i < nSymbols; i++) {
reg1[0] = binarySource[i * CodInB];
reg2[0] = binarySource[i * CodInB + 1];
reg3[0] = binarySource[i * CodInB + 2];
reg4[0] = binarySource[i * CodInB + 3];
byte xor1 = (byte) ((reg1[0] + reg1[1] + reg1[2] + reg1[3] + reg1[4]) % 2);
byte xor2 = (byte) ((reg1[0] + reg1[2] + reg1[3] + reg1[4]) % 2);
byte xor3 = (byte) ((reg2[0] + reg2[1] + reg2[2] + reg2[3]) % 2);
byte xor4 = (byte) ((reg3[0] + reg3[1] + reg3[3] + reg3[4]) % 2);
byte xor5 = (byte) ((reg3[0] + reg3[2] + reg3[4]) % 2);
byte xor6 = (byte) ((reg4[0] + reg4[2] + reg4[3]) % 2);
byte xor7 = (byte) ((reg4[0] + reg4[1] + reg4[3]) % 2);
codedSource[i][0] = xor1;
codedSource[i][1] = (byte) ((xor2 + xor3) % 2);
codedSource[i][2] = (byte) ((xor3 + xor4) % 2);
codedSource[i][3] = (byte) ((xor5 + xor6) % 2);
codedSource[i][4] = xor7;
// one-bit Right Shift of each register
reg1[4] = reg1[3];
reg3[4] = reg3[3];
for (int j = 3; j > 0; j--) {
reg1[j] = reg1[j - 1];
reg2[j] = reg2[j - 1];
reg3[j] = reg3[j - 1];
reg4[j] = reg4[j - 1];
}
}
break;
// ------------------------------------------------------
case 2: // coding rate 2/3
for (int i = 0; i < nSymbols; i++) {
reg1[0] = binarySource[i * CodInB];
reg2[0] = binarySource[i * CodInB + 1];
byte xor1 = (byte) ((reg1[0] + reg1[3] + reg1[4]) % 2);
byte xor2 = (byte) ((reg1[0] + reg1[1] + reg1[2] + reg1[4]) % 2);
byte xor3 = (byte) ((reg2[1] + reg2[3]) % 2);
byte xor4 = (byte) ((reg2[0] + reg2[2] + reg2[3]) % 2);
codedSource[i][0] = xor1;
codedSource[i][1] = (byte) ((xor2 + xor3) % 2);
codedSource[i][2] = xor4;
// one-bit Right Shift of each register
reg1[4] = reg1[3];
for (int j = 3; j > 0; j--) {
reg1[j] = reg1[j - 1];
reg2[j] = reg2[j - 1];
}
}
break;
// ------------------------------------------------------
case 3: // coding rate 4/7
for (int i = 0; i < nSymbols; i++) {
reg1[0] = binarySource[i * CodInB];
reg2[0] = binarySource[i * CodInB + 1];
reg3[0] = binarySource[i * CodInB + 2];
reg4[0] = binarySource[i * CodInB + 3];
byte xor1 = (byte) ((reg1[0] + reg1[1] + reg1[2] + reg1[3] + reg1[4]) % 2);
byte xor2 = (byte) ((reg1[0] + reg1[2] + reg1[3] + reg1[4]) % 2);
byte xor3 = (byte) ((reg2[0] + reg2[1] + reg2[2] + reg2[3]) % 2);
byte xor4 = (byte) ((reg3[0] + reg3[2] + reg3[4]) % 2);
byte xor5 = (byte) ((reg3[0] + reg3[1] + reg3[2] + reg3[4]) % 2);
byte xor6 = (byte) ((reg4[0] + reg4[2] + reg4[3]) % 2);
byte xor7 = (byte) ((reg4[0] + reg4[1] + reg4[3]) % 2);
byte xor8 = (byte) ((reg1[0] + reg1[1] + reg1[3] + reg1[4]) % 2);
byte xor9 = (byte) ((reg2[0] + reg2[2] + reg2[3]) % 2);
byte xor10 = (byte) ((reg3[0] + reg3[1] + reg3[2] + reg3[3] + reg3[4]) % 2);
codedSource[i][0] = xor1;
codedSource[i][1] = (byte) ((xor2 + xor3) % 2);
codedSource[i][2] = (byte) ((xor3 + xor4) % 2);
codedSource[i][3] = (byte) ((xor5 + xor6) % 2);
codedSource[i][4] = xor7;
codedSource[i][5] = (byte) ((xor7 + xor8) % 2);
codedSource[i][6] = (byte) ((xor9 + xor10) % 2);
// one-bit Right Shift of each register
reg1[4] = reg1[3];
reg3[4] = reg3[3];
for (int j = 3; j > 0; j--) {
reg1[j] = reg1[j - 1];
reg2[j] = reg2[j - 1];
reg3[j] = reg3[j - 1];
reg4[j] = reg4[j - 1];
}
}
break;
// ------------------------------------------------------
case 4: // coding rate 1/2
for (int i = 0; i < nSymbols; i++) {
reg1[0] = binarySource[i * CodInB];
codedSource[i][0] = (byte) ((reg1[0] + reg1[3] + reg1[4]) % 2);
codedSource[i][1] = (byte) ((reg1[0] + reg1[1] + reg1[2] + reg1[4]) % 2);
// one-bit Right Shift of each register
for (int j = 4; j > 0; j--) {
reg1[j] = reg1[j - 1];
}
}
break;
}
if (CR != 0) {
for (int i = 0; i < nSymbols; i++) {
for (int j = 0; j < CodOutB; j++) {
codedSourceOut[i * CodOutB + j] = codedSource[i][j];
}
}
}
return codedSourceOut;
}
// ------------------------------------------------------
// Hadamard Code Generator
public static double[][] generateHadamard(int codelength) {
double[][] hadamard;
hadamard = new double[codelength][codelength];
hadamard[0][0] = 1;
for (int k = 1; k < codelength; k += k) {
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
hadamard[i + k][j] = hadamard[i][j];
hadamard[i][j + k] = hadamard[i][j];
hadamard[i + k][j + k] = -1 * hadamard[i][j];
}
}
}
return hadamard;
}
public static double[] getcode(int codelength) {
return generateHadamard(codelength)[codelength - 1];
}
// ------------------------------------------------------
// AWGN Channel
public static double[] AWGNchannel(int SF, double codingRate, double[] spreadingCode, double[] txSignal) {
// Energy Profile
double eB = 0.5; // Energy per bit also Energy per symbol Es
double SNR = -60; // Signal to noise ratio
double eBN0 = SNR - Math.log(codingRate * (SF / spreadingCode.length));
double N0 = eB * Math.pow(10, (-eBN0 / 10)); // Noise power
// ------------------------------------------------------
double[] h = { 1.0, 0, 0, 0, 0, 0, 0, 0, 0.7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.3 };
double[] sm = filter(h, txSignal);
double[] channelSignal = new double[txSignal.length];
for (int i = 0; i < txSignal.length; i++) {
Random random = new Random();
channelSignal[i] = Math.sqrt(N0) * random.nextGaussian() + sm[i];
}
return channelSignal;
}
// ------------------------------------------------------
// Filter
public static double[] filter(double[] h, double[] txSignal) {
double[] filteredData;
int n = txSignal.length;
int m = Math.min(h.length, txSignal.length);
filteredData = new double[n];
for (int k = 0; k < n; k++) {
filteredData[k] = 0;
int v = k < m ? k + 1 : m;
for (int i = 0; i < v; i++) {
filteredData[k] += h[i] * txSignal[k - i];
}
}
return filteredData;
}
// ------------------------------------------------------
// converting Input String to Binary bits Function
public static byte[] getStringToBinary(String inputMsg) {
String s2 = "";
for (int i = 0; i < inputMsg.length(); i++) {
char c = inputMsg.charAt(i);
s2 += String.format("%8s", Integer.toBinaryString(c)).replaceAll(" ", "0");
}
s2 = s2.replaceAll("0", "\0");
s2 = s2.replaceAll("1", "\1");
byte[] binarySource = s2.getBytes();
return binarySource;
}
}