[Orangepi]wifi模块异常不能用

执行如下sh文件
#!/bin/bash
# 用法: sudo ./fix_wifi.sh [SSID] [PASSWORD]
set -e

IF=”wlx7822884b2787″
SSID=”${1:-rtlwap}”
PASSWORD=”${2:-}”

echo “=== 1) 基本信息 ===”
echo “日期: $(date)”
uname -r
echo “—— lsmod 相关模块 ——”
lsmod | egrep -i ‘wl|rtl|8812|88xx|88|rtw|wlanuav’ || true
echo “—— ifconfig/ip link ——”
ip -c addr show “$IF” || ip -c link show
echo “—— iw dev (无线设备) ——”
iw dev || true

echo
echo “=== 2) 查看内核日志(相关条目) ===”
sudo dmesg | egrep -i “$IF|0bda|a81a|rtl88|8812|88×2|rtw|wlanuav|usb” | tail -n 200 || true

echo
echo “=== 3) 确保 RFKILL 未阻塞 ===”
rfkill list || true
echo “尝试解除软件阻塞…”
sudo rfkill unblock all || true
sleep 1
rfkill list || true

echo
echo “=== 4) 关闭驱动/设备的省电(iw)并设置国家码 ===”
sudo iw reg set SG || true
sudo iw dev “$IF” set power_save off || true

echo
echo “=== 5) 让 NetworkManager 管理该接口 ===”
sudo nmcli device set “$IF” managed yes || true
sudo systemctl restart NetworkManager
sleep 1
nmcli device status

echo
echo “=== 6) 重新加载模块(如果模块不稳定可尝试) ===”
# 尝试找到模块并重载(安全尝试)
MODNAME=$(lsmod | egrep -o ‘rtl[[:alnum:]_]*|88[[:alnum:]_]*|wl[a-z0-9_]*’ | head -n1 || true)
if [ -n “$MODNAME” ]; then
echo “检测到模块: $MODNAME — 尝试重载”
sudo modprobe -r “$MODNAME” 2>/dev/null || true
sleep 1
sudo modprobe “$MODNAME” 2>/dev/null || true
else
echo “未检测到可疑模块名称,跳过自动重载”
fi
sleep 1

echo
echo “=== 7) 重置接口并扫描可用 Wi‑Fi ===”
sudo ip link set “$IF” down || true
sleep 1
sudo ip link set “$IF” up || true
sleep 1
echo “扫描(ifname=$IF)…”
sudo nmcli device wifi rescan ifname “$IF” || true
sudo nmcli device wifi list ifname “$IF” || true

echo
echo “=== 8) 清理旧同名连接并尝试连接 ===”
# 删除所有同名的 rtlwap 连接,避免冲突
for id in $(nmcli -t -f NAME,UUID connection show | awk -F: -v s=”$SSID” ‘$1 ~ “^”s {print $2}’); do
echo “删除旧连接 $id”
sudo nmcli connection delete “$id” || true
done

if [ -z “$PASSWORD” ]; then
echo “未提供密码,尝试无密码连接(若是有密码请以第二个参数提供)”
sudo nmcli device wifi connect “$SSID” ifname “$IF” || true
else
echo “尝试连接 SSID=’$SSID’(ifname=$IF)…”
sudo nmcli device wifi connect “$SSID” password “$PASSWORD” ifname “$IF” || true
fi

echo
echo “=== 9) 连接状态与最后日志摘要 ===”
nmcli device status
nmcli connection show –active || true
echo “—– dmesg 相关(尾部 200 行) —–”
sudo dmesg | tail -n 200
echo “如果连接失败,请把上面输出全贴过来(特别是 dmesg 与 nmcli device status)”

[Qt]QML地图-4

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtPositioning 5.15

Item {
id: root
width: 640
height: 480

property var baseCoordinate: { “latitude”: 36.35, “longitude”: 117.0 }
property var centerCoordinate: { “latitude”: 36.50, “longitude”: 117.2 } // 中心坐标
property int tileSize: 512
property int zoomLevel: 16
property string tileRoot: “file:///home/orangepi/Layer”
property var tilesCache: ({}) // 缓存瓦片对象

// 外部传入的旋转角度(单位:度)
property real markerAngle: 45

// 猴子标记
Image {
id: monkeyMarker
width: 30
height: 30
source: “qrc:/monkey.png”
z: 10
transformOrigin: Item.Center // 旋转中心在图片中点
rotation: root.markerAngle // 使用外部传入的角度
}

// 画红线
Canvas {
id: lineCanvas
anchors.fill: parent
z: 15
onPaint: {
var ctx = getContext(“2d”);
ctx.clearRect(0, 0, width, height);

ctx.lineWidth = 1;
ctx.strokeStyle = “red”;

var p1 = getPixelPos(root.baseCoordinate);
var p2 = getPixelPos(root.centerCoordinate);

ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
}
}

Flickable {
id: flick
anchors.fill: parent
clip: true
interactive: true
contentWidth: 20000
contentHeight: 20000

Item {
id: tileContainer
width: flick.contentWidth
height: flick.contentHeight
}
}

// 经纬度 → 瓦片坐标
function coordToPixel(lat, lon) {
var zoom = root.zoomLevel;
var n = Math.pow(2, zoom);

var x = (lon + 180) / 360 * n;
var y = (1 – Math.log(Math.tan(lat * Math.PI / 180) +
1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * n;
return { “x”: x, “y”: y };
}

// 转屏幕像素坐标
function getPixelPos(coord) {
var centerTile = coordToPixel(root.centerCoordinate.latitude, root.centerCoordinate.longitude);
var pointTile = coordToPixel(coord.latitude, coord.longitude);

var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;

var startX = Math.floor(centerTile.x – tilesX / 2);
var startY = Math.floor(centerTile.y – tilesY / 2);

var px = (pointTile.x – startX) * root.tileSize + tileContainer.x;
var py = (pointTile.y – startY) * root.tileSize + tileContainer.y;
return { “x”: px, “y”: py };
}

// 更新猴子位置(不再计算角度)
function updateMarkerPosition() {
var center = root.centerCoordinate;
var centerTile = coordToPixel(center.latitude, center.longitude);

var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;

var startX = Math.floor(centerTile.x – tilesX / 2);
var startY = Math.floor(centerTile.y – tilesY / 2);

// 计算中心点像素位置
var px = (centerTile.x – startX) * root.tileSize + tileContainer.x;
var py = (centerTile.y – startY) * root.tileSize + tileContainer.y;

monkeyMarker.x = px – monkeyMarker.width / 2;
monkeyMarker.y = py – monkeyMarker.height / 2;
}

// 更新瓦片
function updateVisibleTiles() {
if (!tileContainer || !root.centerCoordinate) return;

var zoom = root.zoomLevel;
var n = Math.pow(2, zoom);
var center = root.centerCoordinate;

var centerTile = coordToPixel(center.latitude, center.longitude);

var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;

var startX = Math.floor(centerTile.x – tilesX / 2);
var startY = Math.floor(centerTile.y – tilesY / 2);

var currentKeys = {};
for (var x = startX; x < startX + tilesX; x++) { for (var y = startY; y < startY + tilesY; y++) { var key = x + "_" + y; currentKeys[key] = true; var path = root.tileRoot + "/" + zoom + "/" + x + "/" + y + ".png"; if (!tilesCache[key]) { var tile = Qt.createQmlObject( 'import QtQuick 2.15; Image { width:' + root.tileSize + '; height:' + root.tileSize + '; source: "' + path + '"; fillMode: Image.PreserveAspectFit }', tileContainer ); tilesCache[key] = tile; } var tileObj = tilesCache[key]; tileObj.x = (x - startX) * root.tileSize; tileObj.y = (y - startY) * root.tileSize; } } // 删除多余瓦片 for (var key in tilesCache) { if (!currentKeys[key]) { tilesCache[key].destroy(); delete tilesCache[key]; } } // 平移 tileContainer,使中心瓦片对齐 var offsetX = (centerTile.x - startX) * root.tileSize - flick.width / 2; var offsetY = (centerTile.y - startY) * root.tileSize - flick.height / 2; tileContainer.x = -offsetX; tileContainer.y = -offsetY; updateMarkerPosition(); lineCanvas.requestPaint(); // 重新画红线 } Component.onCompleted: updateVisibleTiles() }

[Python]检查瓦片连续性

import os
import argparse
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import time

def check_tile_continuity_practical(base_path, target_z_levels=None):
“””
检查瓦片文件的连续性(基于实际存在的X文件夹,检查Y的连续性包括边界)

Args:
base_path: 瓦片文件根目录
target_z_levels: 要检查的z层级列表,默认为[10, 12, 14, 16]

Returns:
dict: 包含检查结果的字典
“””
if target_z_levels is None:
target_z_levels = [10, 12, 14, 16]

results = {}

for z in target_z_levels:
z_path = Path(base_path) / str(z)
print(f”正在检查层级 z={z}…”)

if not z_path.exists():
print(f” 警告: 层级 {z} 的目录不存在: {z_path}”)
results[z] = {‘status’: ‘missing’, ‘message’: f’目录不存在: {z_path}’}
continue

# 获取所有x子目录
x_dirs = [d for d in z_path.iterdir() if d.is_dir() and d.name.isdigit()]

if not x_dirs:
print(f” 警告: 层级 {z} 下没有找到x子目录”)
results[z] = {‘status’: ’empty’, ‘message’: ‘没有x子目录’}
continue

x_dirs.sort(key=lambda x: int(x.name))
x_values = [int(x_dir.name) for x_dir in x_dirs]

print(f” 实际X范围: {min(x_values)}-{max(x_values)}, 共{len(x_values)}个目录”)

# 检测该层级的Y理论范围(基于所有X目录的Y文件)
y_global_range = detect_y_global_range(x_dirs)
if y_global_range:
print(f” 检测到Y全局范围: {y_global_range[0]}-{y_global_range[1]}”)
else:
print(f” 警告: 无法检测到Y全局范围”)
y_global_range = (0, 0)

y_continuity_issues = []
total_tiles = 0
missing_tiles = 0

# 检查每个x目录下的y文件连续性
for x_dir in x_dirs:
x_value = int(x_dir.name)

# 获取所有y.png文件
y_files = list(x_dir.glob(“*.png”))
y_values = []

for y_file in y_files:
if y_file.stem.isdigit():
y_values.append(int(y_file.stem))

if not y_values:
# 空目录 – 如果检测到全局范围,则整个范围都缺失
if y_global_range and y_global_range[0] != y_global_range[1]:
all_missing_y = list(range(y_global_range[0], y_global_range[1] + 1))
missing_tiles += len(all_missing_y)
y_continuity_issues.append({
‘x’: x_value,
‘missing_y’: all_missing_y,
‘y_range’: y_global_range,
‘expected_count’: y_global_range[1] – y_global_range[0] + 1,
‘actual_count’: 0,
‘note’: ‘空目录’
})
else:
y_continuity_issues.append({
‘x’: x_value,
‘missing_y’: [],
‘y_range’: (0, 0),
‘expected_count’: 0,
‘actual_count’: 0,
‘note’: ‘空目录’
})
continue

y_values.sort()

# 使用全局范围检查Y的连续性(包括边界)
if y_global_range and y_global_range[0] != y_global_range[1]:
y_min, y_max = y_global_range
y_missing = check_number_continuity_with_bounds(y_values, y_min, y_max)
expected_count = y_max – y_min + 1
else:
# 如果没有全局范围,使用该目录的实际范围
y_min, y_max = min(y_values), max(y_values)
y_missing = check_number_continuity(y_values)
expected_count = len(y_values) + len(y_missing)

total_tiles += len(y_values)

if y_missing:
missing_tiles += len(y_missing)
y_continuity_issues.append({
‘x’: x_value,
‘missing_y’: y_missing,
‘y_range’: (y_min, y_max),
‘expected_count’: expected_count,
‘actual_count’: len(y_values),
‘note’: ‘部分缺失’
})

# 汇总结果
results[z] = {
‘status’: ‘checked’,
‘actual_x_range’: (min(x_values), max(x_values)) if x_values else (0, 0),
‘y_global_range’: y_global_range,
‘x_count’: len(x_values),
‘x_missing’: [], # 不检查X的连续性,只基于实际存在的X目录
‘total_tiles’: total_tiles,
‘missing_tiles’: missing_tiles,
‘y_continuity_issues’: y_continuity_issues,
‘completeness_rate’: (total_tiles / (total_tiles + missing_tiles)) * 100 if (total_tiles + missing_tiles) > 0 else 0,
‘method’: ‘practical’
}

return results

def detect_y_global_range(x_dirs, sample_limit=30):
“””
检测该层级所有X目录的Y全局范围(不扩展边界)

Args:
x_dirs: X目录列表
sample_limit: 最大抽样目录数量

Returns:
tuple: (y_min, y_max) 或 None(如果无法检测)
“””
all_y_values = []

# 等间距抽样以提高覆盖率
sample_count = min(sample_limit, len(x_dirs))
if len(x_dirs) <= sample_count: sampled_dirs = x_dirs # 目录不多时全量检查 else: step = len(x_dirs) // sample_count sampled_dirs = x_dirs[::step] + [x_dirs[-1]] # 确保包含最后一个目录 print(f" 抽样检查 {len(sampled_dirs)} 个X目录的Y范围") for i, x_dir in enumerate(sampled_dirs): y_files = list(x_dir.glob("*.png")) y_values = [int(f.stem) for f in y_files if f.stem.isdigit()] if y_values: dir_min = min(y_values) dir_max = max(y_values) all_y_values.extend(y_values) if i < 3: # 显示前3个目录的范围作为参考 print(f" X={x_dir.name}: Y范围 {dir_min}-{dir_max}") if not all_y_values: return None # 直接使用检测到的最小最大值,不扩展边界 y_min = min(all_y_values) y_max = max(all_y_values) print(f" 最终Y全局范围: {y_min}-{y_max}") return (y_min, y_max) def check_number_continuity_with_bounds(numbers, expected_min, expected_max): """ 检查数字序列的连续性,包括边界检测 Args: numbers: 现有的数字列表 expected_min: 预期的最小值 expected_max: 预期的最大值 Returns: list: 缺失的数字列表(包括边界缺失) """ if not numbers: return list(range(expected_min, expected_max + 1)) existing_set = set(numbers) expected_set = set(range(expected_min, expected_max + 1)) missing = sorted(list(expected_set - existing_set)) return missing def check_number_continuity(numbers): """ 检查数字序列的连续性(基于实际范围) Args: numbers: 排序后的数字列表 Returns: list: 缺失的数字列表 """ if not numbers: return [] min_val = min(numbers) max_val = max(numbers) expected_set = set(range(min_val, max_val + 1)) actual_set = set(numbers) missing = sorted(list(expected_set - actual_set)) return missing def print_results(results): """打印检查结果""" print("\n" + "="*80) print("瓦片文件连续性检查报告(基于实际X目录,包含Y边界检测)") print("="*80) total_actual_tiles = 0 total_missing_tiles = 0 for z, data in results.items(): print(f"\n层级 z={z}:") if data['status'] != 'checked': print(f" ⚠️ {data['message']}") continue print(f" 实际X范围: {data['actual_x_range'][0]}-{data['actual_x_range'][1]}") if data['y_global_range']: print(f" Y全局范围: {data['y_global_range'][0]}-{data['y_global_range'][1]}") print(f" X目录数量: {data['x_count']}") print(f" 总瓦片数: {data['total_tiles']:,}") print(f" 缺失瓦片数: {data['missing_tiles']:,}") print(f" 完整度: {data['completeness_rate']:.2f}%") total_actual_tiles += data['total_tiles'] total_missing_tiles += data['missing_tiles'] if data['y_continuity_issues']: empty_dirs = sum(1 for issue in data['y_continuity_issues'] if issue.get('note') == '空目录') partial_dirs = sum(1 for issue in data['y_continuity_issues'] if issue.get('note') == '部分缺失') print(f" ⚠️ 发现 {len(data['y_continuity_issues'])} 个问题目录:") if empty_dirs > 0:
print(f” 空目录: {empty_dirs}个”)
if partial_dirs > 0:
print(f” Y不连续目录: {partial_dirs}个”)

# 显示部分缺失目录的详细信息
partial_issues = [issue for issue in data[‘y_continuity_issues’] if issue.get(‘note’) == ‘部分缺失’]
if partial_issues:
print(f”\n Y不连续目录详情:”)
for issue in partial_issues[:5]: # 显示前5个
boundary_missing = []
if issue[‘missing_y’]:
y_min, y_max = issue[‘y_range’]
boundary_missing = [y for y in issue[‘missing_y’] if y == y_min or y == y_max]

print(f” X={issue[‘x’]}: Y范围{issue[‘y_range’]}, 缺失{len(issue[‘missing_y’])}个文件”)
if boundary_missing:
print(f” 边界缺失: {boundary_missing}”)
if len(issue[‘missing_y’]) <= 10: print(f" 所有缺失Y: {issue['missing_y']}") else: print(f" 缺失Y示例: {issue['missing_y'][:5]} ... {issue['missing_y'][-5:]}") if len(partial_issues) > 5:
print(f” … 还有 {len(partial_issues) – 5} 个目录”)
else:
print(f” ✅ 所有Y轴文件连续完整”)

# 汇总统计
print(“\n” + “=”*80)
print(“汇总统计”)
print(“=”*80)
total_files = total_actual_tiles + total_missing_tiles
overall_completeness = (total_actual_tiles / total_files) * 100 if total_files > 0 else 0
print(f”总瓦片数: {total_files:,}”)
print(f”实际瓦片数: {total_actual_tiles:,}”)
print(f”缺失瓦片数: {total_missing_tiles:,}”)
print(f”总体完整度: {overall_completeness:.2f}%”)

def save_results_to_txt(results, output_file=”tile_check_results.txt”):
“””将详细检查结果保存到txt文件”””
with open(output_file, ‘w’, encoding=’utf-8′) as f:
f.write(“=” * 80 + “\n”)
f.write(“瓦片文件连续性检查报告(基于实际X目录,包含Y边界检测)\n”)
f.write(“=” * 80 + “\n”)
f.write(f”生成时间: {time.strftime(‘%Y-%m-%d %H:%M:%S’)}\n”)
f.write(“说明: 基于实际存在的X目录检查Y文件的连续性,包含边界检测\n\n”)

total_actual_tiles = 0
total_missing_tiles = 0
checked_levels = 0

for z, data in results.items():
f.write(f”\n层级 z={z}:\n”)
f.write(“-” * 50 + “\n”)

if data[‘status’] != ‘checked’:
f.write(f”状态: {data[‘message’]}\n”)
continue

checked_levels += 1
f.write(f”实际X范围: {data[‘actual_x_range’][0]}-{data[‘actual_x_range’][1]}\n”)
if data[‘y_global_range’]:
f.write(f”Y全局范围: {data[‘y_global_range’][0]}-{data[‘y_global_range’][1]}\n”)
f.write(f”X目录数量: {data[‘x_count’]}\n”)
f.write(f”总瓦片数: {data[‘total_tiles’]:,}\n”)
f.write(f”缺失瓦片数: {data[‘missing_tiles’]:,}\n”)
f.write(f”完整度: {data[‘completeness_rate’]:.2f}%\n”)

total_actual_tiles += data[‘total_tiles’]
total_missing_tiles += data[‘missing_tiles’]

# Y轴连续性问题
if data[‘y_continuity_issues’]:
empty_dirs = [issue for issue in data[‘y_continuity_issues’] if issue.get(‘note’) == ‘空目录’]
partial_dirs = [issue for issue in data[‘y_continuity_issues’] if issue.get(‘note’) == ‘部分缺失’]

f.write(f”\nY轴连续性问题:\n”)
f.write(f” 空目录: {len(empty_dirs)}个\n”)
f.write(f” Y不连续目录: {len(partial_dirs)}个\n”)

# 详细列出空目录
if empty_dirs:
f.write(f”\n 空目录列表:\n”)
for issue in empty_dirs:
f.write(f” X={issue[‘x’]}\n”)

# 详细列出部分缺失目录
if partial_dirs:
f.write(f”\n Y不连续目录详情:\n”)
for issue in partial_dirs:
# 检查边界缺失
boundary_missing = []
if issue[‘missing_y’]:
y_min, y_max = issue[‘y_range’]
boundary_missing = [y for y in issue[‘missing_y’] if y == y_min or y == y_max]

f.write(f” X={issue[‘x’]}: Y范围{issue[‘y_range’]}, 应有{issue[‘expected_count’]}个, 实有{issue[‘actual_count’]}个, 缺失{len(issue[‘missing_y’])}个\n”)
if boundary_missing:
f.write(f” 边界缺失: {boundary_missing}\n”)
if len(issue[‘missing_y’]) <= 20: f.write(f" 所有缺失Y: {issue['missing_y']}\n") else: f.write(f" 缺失Y: {issue['missing_y'][:10]} ... {issue['missing_y'][-10:]} (共{len(issue['missing_y'])}个)\n") else: f.write("Y轴文件: 连续完整 ✓\n") f.write("\n") # 汇总统计 f.write("\n" + "=" * 80 + "\n") f.write("汇总统计\n") f.write("=" * 80 + "\n") f.write(f"检查层级数量: {checked_levels}\n") total_files = total_actual_tiles + total_missing_tiles f.write(f"总瓦片数: {total_files:,}\n") f.write(f"实际瓦片数: {total_actual_tiles:,}\n") f.write(f"缺失瓦片数: {total_missing_tiles:,}\n") if total_files > 0:
overall_completeness = (total_actual_tiles / total_files) * 100
f.write(f”总体完整度: {overall_completeness:.2f}%\n”)

print(f”详细报告已保存到: {output_file}”)

def save_missing_list(results, output_file=”missing_tiles.txt”):
“””保存缺失瓦片列表到文件”””
with open(output_file, ‘w’, encoding=’utf-8′) as f:
f.write(“# 缺失瓦片列表(基于实际X目录,包含Y边界检测)\n”)
f.write(“# 格式: z/x/y\n”)
f.write(“# 生成时间: {}\n”.format(time.strftime(“%Y-%m-%d %H:%M:%S”)))
f.write(“# 说明: 列出实际存在的X目录中缺失的Y文件,包括边界缺失\n”)
f.write(“=”*50 + “\n”)

total_missing = 0
for z, data in results.items():
if data[‘status’] == ‘checked’ and data[‘y_continuity_issues’]:
f.write(f”\n# 层级 z={z}\n”)

# 记录所有缺失的Y文件
for issue in data[‘y_continuity_issues’]:
if issue.get(‘note’) in [‘部分缺失’, ‘空目录’]:
for missing_y in issue[‘missing_y’]:
f.write(f”{z}/{issue[‘x’]}/{missing_y}\n”)
total_missing += 1

f.write(f”\n# 总共缺失 {total_missing} 个瓦片文件\n”)

print(f”缺失列表已保存到: {output_file}”)

def main():
parser = argparse.ArgumentParser(description=’检查瓦片文件连续性(基于实际X目录,包含Y边界检测)’)
parser.add_argument(‘path’, help=’瓦片文件根目录路径’)
parser.add_argument(‘-z’, ‘–zoom-levels’, nargs=’+’, type=int,
default=[10, 12, 14, 16], help=’要检查的z层级,默认为10 12 14 16′)
parser.add_argument(‘-o’, ‘–output’, default=’tile_check_results.txt’,
help=’输出结果文件名’)

args = parser.parse_args()

if not os.path.exists(args.path):
print(f”错误: 路径不存在: {args.path}”)
return

print(f”检查路径: {args.path}”)
print(f”检查层级: {args.zoom_levels}”)
print(“检查方法: 基于实际X目录检查Y连续性(包含边界检测)”)
print(“开始检查…” + “\n”)

start_time = time.time()

results = check_tile_continuity_practical(args.path, args.zoom_levels)

end_time = time.time()

print_results(results)

# 保存详细报告
save_results_to_txt(results, args.output)

# 保存缺失列表
missing_list_file = args.output.replace(‘.txt’, ‘_missing_list.txt’)
save_missing_list(results, missing_list_file)

print(f”\n检查完成! 耗时: {end_time – start_time:.2f}秒”)

if __name__ == “__main__”:
base_path = r”E:\Map\Layer_2025-09-27_231600\Layer”

# 使用改进的方法检查
print(“=== 基于实际X目录检查Y连续性(包含边界检测)===”)
results = check_tile_continuity_practical(base_path)
print_results(results)
save_results_to_txt(results, “improved_check_results.txt”)
save_missing_list(results, “improved_missing_list.txt”)

[Qt]QML文件-2

void MainWindow::updateCoordinates(double lat, double lon)
{
QQuickWidget *quickWidget = ui->quickWidget;
if (!quickWidget) return;

QQuickItem *rootObject = quickWidget->rootObject();
if (!rootObject) return;

QVariantMap coord;
coord[“latitude”] = lat;
coord[“longitude”] = lon;
rootObject->setProperty(“centerCoordinate”, coord);

QMetaObject::invokeMethod(rootObject, “updateVisibleTiles”);
}

//QML

import QtQuick 2.15
import QtQuick.Controls 2.15

Item {
id: root
width: 640
height: 480

property var centerCoordinate: { “latitude”: 36.35, “longitude”: 117.0 }
property int tileSize: 512
property int zoomLevel: 16
property string tileRoot: “file:///home/orangepi/Layer”

Flickable {
id: flick
anchors.fill: parent
clip: true
interactive: true
contentWidth: 20000
contentHeight: 20000

Item {
id: tileContainer
width: flick.contentWidth
height: flick.contentHeight
property var tilesCache: ({}) // 缓存瓦片对象
}

Rectangle {
id: redDot
width: 12
height: 12
color: “red”
radius: 6
anchors.centerIn: parent
}

onContentXChanged: updateVisibleTiles()
onContentYChanged: updateVisibleTiles()
}

function updateVisibleTiles() {
if (!tileContainer || !root.centerCoordinate) return;

var zoom = root.zoomLevel;
var n = Math.pow(2, zoom);
var center = root.centerCoordinate;

// 中心瓦片浮点坐标
var centerTileXf = (center.longitude + 180) / 360 * n;
var centerTileYf = (1 – Math.log(Math.tan(center.latitude*Math.PI/180) + 1/Math.cos(center.latitude*Math.PI/180)) / Math.PI) / 2 * n;

var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;

var startX = Math.floor(centerTileXf – tilesX/2);
var startY = Math.floor(centerTileYf – tilesY/2);

// 记录当前视野瓦片 key
var currentKeys = {};
for (var x = startX; x < startX + tilesX; x++) { for (var y = startY; y < startY + tilesY; y++) { var key = x + "_" + y; currentKeys[key] = true; var path = root.tileRoot + "/" + zoom + "/" + x + "/" + y + ".png"; if (!tileContainer.tilesCache[key]) { var tile = Qt.createQmlObject( 'import QtQuick 2.15; Image { width:' + root.tileSize + '; height:' + root.tileSize + '; source: "' + path + '"; fillMode: Image.PreserveAspectFit }', tileContainer ); tileContainer.tilesCache[key] = tile; } // 更新位置 var tileObj = tileContainer.tilesCache[key]; tileObj.x = (x - startX) * root.tileSize; tileObj.y = (y - startY) * root.tileSize; } } // 删除超出视野的瓦片 for (var key in tileContainer.tilesCache) { if (!currentKeys[key]) { tileContainer.tilesCache[key].destroy(); delete tileContainer.tilesCache[key]; } } // 移动 tileContainer,使中心瓦片在 Flickable 中心 var offsetX = (centerTileXf - startX) * root.tileSize - flick.width/2; var offsetY = (centerTileYf - startY) * root.tileSize - flick.height/2; tileContainer.x = -offsetX; tileContainer.y = -offsetY; // 红点始终在视口中心 redDot.x = flick.width / 2 - redDot.width/2; redDot.y = flick.height / 2 - redDot.height/2; } Component.onCompleted: updateVisibleTiles() }

[Linux]开机自动挂载TF卡

1.确保已安装 exFAT 支持
sudo apt update
sudo apt install exfat-fuse exfatprogs

2.获取 TF 卡信息,获取UUID等
sudo blkid /dev/mmcblk1p1
比如返回 /dev/mmcblk1p1: UUID=”3931-6530″ BLOCK_SIZE=”512″ TYPE=”exfat”

3.编辑 fstab
sudo vi /etc/fstab

4.在文件末尾添加以下内容
# TF Card Auto Mount (exFAT)
UUID=3931-6530 /mnt/tfcard exfat defaults,uid=1000,gid=1000,umask=000,noatime 0 0

[Qt]QML地图文件

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtLocation 5.15
import QtPositioning 5.15

Item {
width: 800
height: 600

Plugin {
id: mapPlugin
name: “osm” // 使用 OpenStreetMap 插件
}

// 坐标属性,可通过 C++ 更新
property var coordinateA: QtPositioning.coordinate(36.01, 116.11)
property var coordinateB: QtPositioning.coordinate(37.0, 117.0)
property real zoomLevelVal: 10

Map {
anchors.fill: parent
plugin: mapPlugin
center: coordinateA // 默认以 A 为中心
zoomLevel: zoomLevelVal // 使用绑定的 zoomLevel 属性

// 显示从 A 到 B 的路径
MapPolyline {
line.width: 1
line.color: “red”
path: [coordinateA, coordinateB]
}

MapQuickItem {
coordinate: coordinateA // A 坐标点
sourceItem: Image {
width: 30
height: 30
source: “qrc:/monkey.png” // 替换为您的箭头图片
//transformOrigin:Item.Center // 以图片中心为基准点
rotation: Math.atan2(-coordinateB.latitude + coordinateA.latitude,
-coordinateB.longitude + coordinateA.longitude) * 180 / Math.PI
}
}
}
}

[OrangePi]生成镜像

1.下载 git clone https://gitee.com/orangepi-xunlong/opi-bkimg.git
2.进入当前目录下的 opi-bkimg
3.sudo ./opi-bkimg
4.生成img文件在/mnt文件夹

[STM32G4] ADC转换

LL_ADC_EnableIT_EOC(ADC1);
LL_ADC_StartCalibration(ADC1,LL_ADC_SINGLE_ENDED);
while(LL_ADC_IsCalibrationOnGoing(ADC1));
LL_ADC_Enable(ADC1);
LL_ADC_REG_StartConversion(ADC1);

————————

void ADC1_read(uint16_t *pusbuf)
{
LL_ADC_REG_StartConversion(ADC1);
while(LL_ADC_IsActiveFlag_EOC(ADC1) == 0);
pusbuf[0] = LL_ADC_REG_ReadConversionData10(ADC1);
while(LL_ADC_IsActiveFlag_EOC(ADC1) == 0);
pusbuf[1] = LL_ADC_REG_ReadConversionData10(ADC1);
}

[STM32] SPI EEPROM MODE0

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;// 72000000 ~1MHZ
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI3, &SPI_InitStructure);