Clean up some strings and show checksum check
Signed-off-by: Severin Kaderli <severin@kaderli.dev>
This commit is contained in:
parent
308104f321
commit
1a221761b8
7 changed files with 118 additions and 27 deletions
51
src/MagSend/app/src/main/java/dev/kaderli/magsend/Utility.kt
Normal file
51
src/MagSend/app/src/main/java/dev/kaderli/magsend/Utility.kt
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package dev.kaderli.magsend
|
||||||
|
|
||||||
|
class Utility {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Calculates the CRC-8-AUTOSAR checksum of the given array of bytes.
|
||||||
|
*
|
||||||
|
* This code is based on the code examples from the following page:
|
||||||
|
* http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html
|
||||||
|
*
|
||||||
|
* The specifics of the algorithm is coming from the AUTOSAR CRC specification:
|
||||||
|
* https://www.autosar.org/fileadmin/user_upload/standards/classic/21-11/AUTOSAR_SWS_CRCLibrary.pdf
|
||||||
|
*
|
||||||
|
* > Utility.crc8Autosar(arrayListOf(0x0, 0x0, 0x0, 0x0)).toString(16)
|
||||||
|
* "12"
|
||||||
|
* > Utility.crc8Autosar(arrayListOf(0xF2, 0x01, 0x83)).toString(16)
|
||||||
|
* "c2"
|
||||||
|
* > Utility.crc8Autosar(arrayListOf(0x0F, 0xAA, 0x00, 0x55)).toString(16)
|
||||||
|
* "c6"
|
||||||
|
* > Utility.crc8Autosar(arrayListOf(0x00, 0xff, 0x55, 0x11)).toString(16)
|
||||||
|
* "77"
|
||||||
|
* > Utility.crc8Autosar(arrayListOf(0x33, 0x22, 0x55, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff)).toString(16)
|
||||||
|
* "11"
|
||||||
|
* > Utility.crc8Autosar(arrayListOf(0x92, 0x6b, 0x55)).toString(16)
|
||||||
|
* "33"
|
||||||
|
* > Utility.crc8Autosar(arrayListOf(0xff, 0xff, 0xff, 0xff)).toString(16)
|
||||||
|
* "6c"
|
||||||
|
*/
|
||||||
|
fun crc8Autosar(bytes: List<Int>): Int {
|
||||||
|
val polynomial = 0x2f;
|
||||||
|
val xorOut = 0xff;
|
||||||
|
var crc = 0xff;
|
||||||
|
|
||||||
|
for (byte in bytes) {
|
||||||
|
crc = crc xor byte;
|
||||||
|
|
||||||
|
for(i in 1..8) {
|
||||||
|
crc = if ((crc and 0x80) != 0) {
|
||||||
|
((crc shl 1) xor polynomial) and 0xff;
|
||||||
|
} else {
|
||||||
|
crc shl 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc xor xorOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ abstract class BaseSensorActivity : BaseActivity(), SensorEventListener {
|
||||||
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
|
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
|
||||||
|
|
||||||
override fun onSensorChanged(event: SensorEvent) {
|
override fun onSensorChanged(event: SensorEvent) {
|
||||||
|
// Calculate the absolute magnetic field strength
|
||||||
val magneticFieldStrength = sqrt(event.values[0].pow(2) + event.values[1].pow(2) + event.values[2].pow(2))
|
val magneticFieldStrength = sqrt(event.values[0].pow(2) + event.values[1].pow(2) + event.values[2].pow(2))
|
||||||
sensorValueReceived(magneticFieldStrength)
|
sensorValueReceived(magneticFieldStrength)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
package dev.kaderli.magsend.activity
|
package dev.kaderli.magsend.activity
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import dev.kaderli.magsend.R
|
import dev.kaderli.magsend.R
|
||||||
|
import dev.kaderli.magsend.Utility
|
||||||
import dev.kaderli.magsend.model.Sample
|
import dev.kaderli.magsend.model.Sample
|
||||||
import dev.kaderli.magsend.model.Signal
|
import dev.kaderli.magsend.model.Signal
|
||||||
import java.lang.Integer.min
|
import java.lang.Integer.min
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
class ReceiveActivity : BaseSensorActivity() {
|
class ReceiveActivity : BaseSensorActivity() {
|
||||||
|
private lateinit var receiveDescription: TextView
|
||||||
private lateinit var preambleStatus: TextView
|
private lateinit var preambleStatus: TextView
|
||||||
|
private lateinit var crcStatus: TextView
|
||||||
private lateinit var headerStatus: TextView
|
private lateinit var headerStatus: TextView
|
||||||
private lateinit var receiveValue: TextView
|
private lateinit var receiveValue: TextView
|
||||||
|
|
||||||
|
@ -30,9 +33,13 @@ class ReceiveActivity : BaseSensorActivity() {
|
||||||
*/
|
*/
|
||||||
private var preambleReceived = false
|
private var preambleReceived = false
|
||||||
|
|
||||||
|
private var receivingComplete = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
receiveDescription = findViewById(R.id.receiveDescription)
|
||||||
preambleStatus = findViewById(R.id.preambleStatus)
|
preambleStatus = findViewById(R.id.preambleStatus)
|
||||||
|
crcStatus = findViewById(R.id.crcStatus)
|
||||||
headerStatus = findViewById(R.id.headerStatus)
|
headerStatus = findViewById(R.id.headerStatus)
|
||||||
receiveValue = findViewById(R.id.receiveValue)
|
receiveValue = findViewById(R.id.receiveValue)
|
||||||
}
|
}
|
||||||
|
@ -168,13 +175,13 @@ class ReceiveActivity : BaseSensorActivity() {
|
||||||
var maxSamplesInARow = 0
|
var maxSamplesInARow = 0
|
||||||
var count = 0
|
var count = 0
|
||||||
cleanedSignal.forEachIndexed { index, sample ->
|
cleanedSignal.forEachIndexed { index, sample ->
|
||||||
if(index == 0) {
|
if (index == 0) {
|
||||||
return@forEachIndexed
|
return@forEachIndexed
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sample.value == cleanedSignal[index - 1].value) {
|
if (sample.value == cleanedSignal[index - 1].value) {
|
||||||
count ++
|
count++
|
||||||
if(count > maxSamplesInARow) {
|
if (count > maxSamplesInARow) {
|
||||||
maxSamplesInARow = count
|
maxSamplesInARow = count
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -182,7 +189,6 @@ class ReceiveActivity : BaseSensorActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val packetData = ArrayList<Int>()
|
val packetData = ArrayList<Int>()
|
||||||
var lastSignal: Sample<Signal>? = null
|
var lastSignal: Sample<Signal>? = null
|
||||||
var isFirstSignal = true
|
var isFirstSignal = true
|
||||||
|
@ -193,28 +199,28 @@ class ReceiveActivity : BaseSensorActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sample.value != lastSignal!!.value) {
|
if (sample.value != lastSignal!!.value) {
|
||||||
if(samplesSinceLastSignal > (maxSamplesInARow * 0.75) || isFirstSignal) {
|
if (samplesSinceLastSignal > (maxSamplesInARow * 0.75) || isFirstSignal) {
|
||||||
//Log.i(TAG, "$samplesSinceLastSignal")
|
|
||||||
if (sample.value == Signal.High) {
|
if (sample.value == Signal.High) {
|
||||||
packetData.add(0)
|
packetData.add(0)
|
||||||
} else {
|
} else {
|
||||||
packetData.add(1)
|
packetData.add(1)
|
||||||
}
|
}
|
||||||
isFirstSignal = false
|
isFirstSignal = false
|
||||||
samplesSinceLastSignal=0
|
samplesSinceLastSignal = 0
|
||||||
}
|
}
|
||||||
lastSignal = sample
|
lastSignal = sample
|
||||||
}
|
}
|
||||||
samplesSinceLastSignal++
|
samplesSinceLastSignal++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Log.i(TAG, "$cleanSignal")
|
|
||||||
|
|
||||||
return packetData
|
return packetData
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sensorValueReceived(magneticFieldStrength: Float) {
|
override fun sensorValueReceived(magneticFieldStrength: Float) {
|
||||||
|
if (receivingComplete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Add the current received sensor value to the samples
|
// Add the current received sensor value to the samples
|
||||||
samples.add(Sample(magneticFieldStrength))
|
samples.add(Sample(magneticFieldStrength))
|
||||||
|
|
||||||
|
@ -252,14 +258,26 @@ class ReceiveActivity : BaseSensorActivity() {
|
||||||
for (i in 1..numberOfAvailablePayloadBytes) {
|
for (i in 1..numberOfAvailablePayloadBytes) {
|
||||||
payload += listToInteger(packet.take(4 + (8 * i)).takeLast(8)).toChar().toString()
|
payload += listToInteger(packet.take(4 + (8 * i)).takeLast(8)).toChar().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
receiveValue.text = payload
|
receiveValue.text = payload
|
||||||
|
|
||||||
// TODO: CRC show and calculate if it's correct
|
// TODO: CRC show and calculate if it's correct
|
||||||
if (packet.size >= 4 + (8 * payloadLength) + 8) {
|
if (packet.size >= 4 + (8 * payloadLength) + 8) {
|
||||||
// CRC Check
|
// CRC Check
|
||||||
val crc = listToInteger(packet.take(4 + (8 * payloadLength) + 8).takeLast(8))
|
val receivedCrc = listToInteger(packet.take(4 + (8 * payloadLength) + 8).takeLast(8))
|
||||||
Log.i(TAG, "CRC: $crc")
|
val calculatedCrc =
|
||||||
|
Utility.crc8Autosar(receiveValue.text.codePoints().boxed().collect(Collectors.toList()))
|
||||||
|
|
||||||
|
if (receivedCrc == calculatedCrc) {
|
||||||
|
crcStatus.setText(R.string.crc_status_valid)
|
||||||
|
crcStatus.setTextColor(ContextCompat.getColor(this, R.color.success))
|
||||||
|
} else {
|
||||||
|
crcStatus.setText(R.string.crc_status_invalid)
|
||||||
|
crcStatus.setTextColor(ContextCompat.getColor(this, R.color.error))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark receiving as complete
|
||||||
|
receiveDescription.setText(R.string.receive_description_complete)
|
||||||
|
receivingComplete = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,8 +296,15 @@ class ReceiveActivity : BaseSensorActivity() {
|
||||||
|
|
||||||
fun restartReceiveProcess(view: View) {
|
fun restartReceiveProcess(view: View) {
|
||||||
preambleReceived = false
|
preambleReceived = false
|
||||||
|
receivingComplete = false
|
||||||
|
|
||||||
|
receiveDescription.setText(R.string.receive_description)
|
||||||
preambleStatus.setText(R.string.preamble_status_not_detected)
|
preambleStatus.setText(R.string.preamble_status_not_detected)
|
||||||
preambleStatus.setTextColor(ContextCompat.getColor(this, R.color.error))
|
preambleStatus.setTextColor(ContextCompat.getColor(this, R.color.error))
|
||||||
|
headerStatus.text = ""
|
||||||
|
crcStatus.text = ""
|
||||||
|
receiveValue.text = ""
|
||||||
|
|
||||||
samples.clear()
|
samples.clear()
|
||||||
signal.clear()
|
signal.clear()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/receiveDescription"
|
android:id="@+id/receiveDescription"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/layout_margins"
|
android:layout_marginTop="@dimen/layout_margins"
|
||||||
android:paddingHorizontal="@dimen/layout_margins"
|
android:paddingHorizontal="@dimen/layout_margins"
|
||||||
|
@ -21,10 +21,10 @@
|
||||||
android:id="@+id/preambleStatus"
|
android:id="@+id/preambleStatus"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/component_spacing_small"
|
android:layout_marginTop="@dimen/component_spacing"
|
||||||
android:paddingHorizontal="@dimen/layout_margins"
|
android:paddingHorizontal="@dimen/layout_margins"
|
||||||
android:text="@string/preamble_status_not_detected"
|
android:text="@string/preamble_status_not_detected"
|
||||||
android:textColor="#FF0000"
|
android:textColor="@color/error"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/receiveDescription" />
|
app:layout_constraintTop_toBottomOf="@+id/receiveDescription" />
|
||||||
|
@ -40,21 +40,31 @@
|
||||||
app:layout_constraintTop_toBottomOf="@+id/preambleStatus" />
|
app:layout_constraintTop_toBottomOf="@+id/preambleStatus" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/receiveValue"
|
android:id="@+id/crcStatus"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/component_spacing"
|
android:layout_marginTop="@dimen/component_spacing_small"
|
||||||
android:text="___"
|
android:paddingHorizontal="@dimen/layout_margins"
|
||||||
android:textSize="48sp"
|
android:textColor="@color/success"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/headerStatus" />
|
app:layout_constraintTop_toBottomOf="@+id/headerStatus" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/restartDescription"
|
android:id="@+id/receiveValue"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/component_spacing"
|
android:layout_marginTop="@dimen/component_spacing"
|
||||||
|
android:textSize="48sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/crcStatus" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/restartDescription"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/component_spacing_large"
|
||||||
android:paddingHorizontal="@dimen/layout_margins"
|
android:paddingHorizontal="@dimen/layout_margins"
|
||||||
android:text="@string/restart_description"
|
android:text="@string/restart_description"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="error">#FFFF0000</color>
|
<color name="error">#FFFF5454</color>
|
||||||
<color name="success">#FF00FF00</color>
|
<color name="success">#FF4BB543</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -4,4 +4,5 @@
|
||||||
<dimen name="component_spacing">24dp</dimen>
|
<dimen name="component_spacing">24dp</dimen>
|
||||||
<dimen name="title_text_size">48sp</dimen>
|
<dimen name="title_text_size">48sp</dimen>
|
||||||
<dimen name="component_spacing_small">8dp</dimen>
|
<dimen name="component_spacing_small">8dp</dimen>
|
||||||
|
<dimen name="component_spacing_large">48dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<string name="receive_button_label">Receive</string>
|
<string name="receive_button_label">Receive</string>
|
||||||
<string name="calibrate_button_label">Calibrate</string>
|
<string name="calibrate_button_label">Calibrate</string>
|
||||||
<string name="calibration_description">Open up the website and start the calibration mode. Put your Smartphone on different locations on the sending device and note the value below. The location with the highest value is the most optimal for data transmission.</string>
|
<string name="calibration_description">Open up the website and start the calibration mode. Put your Smartphone on different locations on the sending device and note the value below. The location with the highest value is the most optimal for data transmission.</string>
|
||||||
<string name="receive_description">Currently listening for transmissions. The text that was received so far is displayed below.</string>
|
<string name="receive_description">Currently listening for transmissions.\nThe text that was received so far is displayed below.</string>
|
||||||
<string name="restart_description">To start listening to a new message click the following button.</string>
|
<string name="restart_description">To start listening to a new message click the following button.</string>
|
||||||
<string name="restart_button_label">Restart</string>
|
<string name="restart_button_label">Restart</string>
|
||||||
<string name="calibration_activity_label">Calibration</string>
|
<string name="calibration_activity_label">Calibration</string>
|
||||||
|
@ -14,4 +14,7 @@
|
||||||
<string name="preamble_status_not_detected">Preamble not detected X</string>
|
<string name="preamble_status_not_detected">Preamble not detected X</string>
|
||||||
<string name="preamble_status_detected">Preamble detected ✓</string>
|
<string name="preamble_status_detected">Preamble detected ✓</string>
|
||||||
<string name="payload_length">The payload length is %1$d bytes</string>
|
<string name="payload_length">The payload length is %1$d bytes</string>
|
||||||
|
<string name="receive_description_complete">The transmission is completed.\nThe received text is displayed below.</string>
|
||||||
|
<string name="crc_status_valid">The checksum is valid ✓.</string>
|
||||||
|
<string name="crc_status_invalid">The checksum is invalid X.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Reference in a new issue