package com.flightfeather.uav.model.epw
|
|
import com.flightfeather.uav.common.utils.DateUtil
|
import com.flightfeather.uav.lightshare.bean.DataVo
|
import com.flightfeather.uav.socket.bean.AirData
|
import com.flightfeather.uav.socket.eunm.FactorType
|
|
/**
|
* 突变数据预处理
|
* 1. 针对单个数据突高或突低,前后两个数据的量级接近的情况,进行处理
|
* 2. 针对有关联关系的监测因子,数据量级出现明显错误的情况,进行处理
|
* @date 2025/11/20
|
* @author feiyu02
|
*/
|
class MutationDataPreprocess {
|
|
// 保存数据最大记录数
|
private val MAX_COUNT = 15
|
|
private val lastData = mutableListOf<DataVo>()
|
|
// 量级变化阈值倍数(用于判断是否为突变)
|
private val MUTATION_THRESHOLD = 5.0
|
// 前后数据接近阈值倍数(用于判断前后数据是否量级接近)
|
private val SIMILARITY_THRESHOLD = 1.5
|
|
/**
|
* 数据平滑
|
* 解决单个数据相比前后两个数据的量级过大或过小的问题
|
* @param data 原始数据
|
* @return 预处理后的数据
|
*/
|
fun preprocess(data: List<DataVo>): List<DataVo> {
|
// 当新数据与旧数据采样时间差超过1分钟时,认为两组数据已无关联性,清空旧数据
|
if (lastData.isNotEmpty() && data.isNotEmpty()) {
|
val lastTime = DateUtil.instance.StringToDate(lastData.last().time)
|
val thisTime = DateUtil.instance.StringToDate(data.first().time)
|
if ((thisTime?.time?.minus(lastTime?.time ?: 0) ?: 0) >= (60 * 1000)) {
|
lastData.clear()
|
}
|
}
|
lastData.addAll(data)
|
detectAndReplaceMutation(lastData)
|
saveHistory(lastData)
|
return lastData
|
}
|
|
/**
|
* 检测并替换异常突变数据
|
* @param data 原始数据列表
|
* @return 处理后的数据列表
|
*/
|
fun detectAndReplaceMutation(data: MutableList<DataVo>){
|
if (data.size < 3) return // 数据量不足,无法进行前后对比
|
|
// 遍历数据,从第二个开始到倒数第二个结束
|
for (i in 1 until data.size - 1) {
|
val currentData = data[i]
|
val prevData = data[i - 1]
|
val nextData = data[i + 1]
|
|
// 检查每个监测因子
|
if (currentData.values != null && prevData.values != null && nextData.values != null) {
|
processEachFactor(currentData, prevData, nextData)
|
}
|
}
|
|
}
|
|
/**
|
* 处理每个监测因子的数据突变
|
*/
|
private fun processEachFactor(currentData: DataVo, prevData: DataVo, nextData: DataVo) {
|
val currentFactors = mutableMapOf<String, AirData>()
|
val prevFactors = mutableMapOf<String, AirData>()
|
val nextFactors = mutableMapOf<String, AirData>()
|
|
// 构建因子名称到AirData的映射
|
currentData.values?.forEach { currentFactors[it.factorName ?: ""] = it }
|
prevData.values?.forEach { prevFactors[it.factorName ?: ""] = it }
|
nextData.values?.forEach { nextFactors[it.factorName ?: ""] = it }
|
|
// 遍历当前数据中的所有因子
|
currentFactors.forEach { (factorName, currentFactor) ->
|
val prevFactor = prevFactors[factorName]
|
val nextFactor = nextFactors[factorName]
|
|
// 确保三个数据点都有该因子的数据
|
if (prevFactor != null && nextFactor != null &&
|
currentFactor.factorData != null && prevFactor.factorData != null && nextFactor.factorData != null) {
|
|
val currentValue = currentFactor.factorData!!
|
val prevValue = prevFactor.factorData!!
|
val nextValue = nextFactor.factorData!!
|
|
// 跳过0值或负值,避免除零错误
|
if (prevValue <= 0 || nextValue <= 0) return@forEach
|
|
// 检查是否为异常突变数据
|
if (isMutationData(currentValue, prevValue, nextValue)) {
|
// 替换为前一个数据的值
|
currentFactor.factorData = prevValue
|
}
|
}
|
}
|
}
|
|
/**
|
* 判断当前数据是否为突变数据
|
* 条件:1. 当前数据与前一个数据的量级变化超过阈值
|
* 2. 前一个数据与后一个数据的量级接近
|
*/
|
private fun isMutationData(currentValue: Double, prevValue: Double, nextValue: Double): Boolean {
|
// 计算变化率
|
val currentToPrevRatio = Math.max(currentValue, prevValue) / Math.min(currentValue, prevValue)
|
val prevToNextRatio = Math.max(prevValue, nextValue) / Math.min(prevValue, nextValue)
|
|
// 判断是否满足突变条件
|
return currentToPrevRatio > MUTATION_THRESHOLD && prevToNextRatio <= SIMILARITY_THRESHOLD
|
}
|
|
fun saveHistory(data: MutableList<DataVo>) {
|
// // 将新数据的至多最后15个保存下来(已经过预处理),用于下一次的判断
|
// val newList = mutableListOf<DataVo>()
|
// val s = if ((data.lastIndex - MAX_COUNT + 1) < 0) 0 else data.lastIndex - MAX_COUNT + 1
|
// data.subList(s, data.lastIndex + 1).forEach {
|
// newList.add(it.copy())
|
// }
|
// // 当新数据与旧数据采样时间差超过1分钟时,认为两组数据已无关联性,清空旧数据
|
// if (lastData.isNotEmpty() && newList.isNotEmpty()) {
|
// val lastTime = DateUtil.instance.StringToDate(lastData.last().time)
|
// val thisTime = DateUtil.instance.StringToDate(newList.first().time)
|
// if ((thisTime?.time?.minus(lastTime?.time ?: 0) ?: 0) >= (60 * 1000)) {
|
// lastData.clear()
|
// }
|
// }
|
// lastData.addAll(newList)
|
// 确保保存的数据最多只有最新的15个
|
while (data.size > MAX_COUNT) {
|
data.removeAt(0)
|
}
|
}
|
}
|