offline_check.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #!/usr/bin/env python
  2. '''
  3. Check a set of terrain files for corruption
  4. '''
  5. import os
  6. #from multiprocessing import Pool
  7. from multiprocessing.pool import ThreadPool
  8. import argparse
  9. import time
  10. import gzip
  11. import shutil
  12. import struct
  13. import crc16
  14. from terrain_gen import TERRAIN_GRID_BLOCK_SIZE_Y, east_blocks, IO_BLOCK_SIZE, TERRAIN_GRID_FORMAT_VERSION, GridBlock
  15. # IO block size is 2048
  16. # Actual size is 1821 bytes
  17. # Last 227 bytes is filling
  18. def check_filled(block, lat_int, lon_int, grid_spacing):
  19. '''check a block for validity'''
  20. if len(block) != IO_BLOCK_SIZE - 227:
  21. print("Bad size {0} of {1}".format(len(block), IO_BLOCK_SIZE))
  22. return False
  23. (bitmap, lat, lon, crc, version, spacing) = struct.unpack("<QiiHHH", block[:22])
  24. if(lat == 0 and lon == 0 and crc == 0 and version == 0 and spacing == 0):
  25. #print("Empty block")
  26. return True
  27. if (str(version) != str(TERRAIN_GRID_FORMAT_VERSION)):
  28. print("Bad version: " + str(version))
  29. return False
  30. if abs(lat_int - (lat/1E7)) > 2 or abs(lon_int - (lon/1E7)) > 2:
  31. print("Bad lat/lon: {0}, {1}".format((lat/1E7), (lon/1E7)))
  32. return False
  33. if spacing != 100:
  34. print("Bad spacing: " + str(spacing))
  35. return False
  36. if bitmap != (1<<56)-1:
  37. print("Bad bitmap")
  38. return False
  39. block = block[:16] + struct.pack("<H", 0) + block[18:]
  40. crc2 = crc16.crc16xmodem(block[:1821])
  41. if crc2 != crc:
  42. print("Bad CRC")
  43. return False
  44. # all is good, return lon/lat of block
  45. return (lat, lon)
  46. if __name__ == '__main__':
  47. # Create the parser
  48. parser = argparse.ArgumentParser(description='ArduPilot Terrain DAT file generator')
  49. # Add the arguments
  50. # Folder to store processed DAT files
  51. parser.add_argument('-folder', action="store", dest="folder", default="processedTerrain")
  52. args = parser.parse_args()
  53. targetFolder = os.path.join(os.getcwd(), args.folder)
  54. grid_spacing = 100
  55. #for each file in folder
  56. for file in os.listdir(targetFolder):
  57. if file.endswith("DAT.gz") or file.endswith("DAT"):
  58. # It's a compressed tile
  59. # 1. Check it's a valid gzip
  60. #print(file)
  61. tile = None
  62. try:
  63. lat_int = int(os.path.basename(file)[1:3])
  64. if os.path.basename(file)[0:1] == "S":
  65. lat_int = -lat_int
  66. lon_int = int(os.path.basename(file)[4:7])
  67. if os.path.basename(file)[3:4] == "W":
  68. lon_int = -lon_int
  69. if file.endswith("DAT.gz"):
  70. with gzip.open(os.path.join(targetFolder, file), 'rb') as f:
  71. tile = f.read()
  72. else:
  73. with open(os.path.join(targetFolder, file), 'rb') as f:
  74. tile = f.read()
  75. except Exception as e:
  76. print("Bad file: " + file)
  77. print(e)
  78. # 2. Is it a valid dat file?
  79. if (tile):
  80. # 2a. Are the correct number (integer) of blocks present?
  81. # It will be a multiple of 2048 bytes (block size)
  82. # There is an missing 227 bytes at the end on the file (2048-1821 = 227), as the
  83. # terrain blocks only take up 1821 bytes.
  84. # APM actually adds the padding on the end, so extra 227 is not needed sometimes
  85. total_blocks = 0
  86. if (len(tile)+227) % IO_BLOCK_SIZE == 0:
  87. total_blocks = int((len(tile)+227) / IO_BLOCK_SIZE)
  88. elif len(tile) % IO_BLOCK_SIZE == 0:
  89. total_blocks = int(len(tile) / IO_BLOCK_SIZE)
  90. else:
  91. print("Bad file size: {0}. {1} extra bytes at end".format(file, len(tile), len(tile) % IO_BLOCK_SIZE))
  92. if total_blocks > 6000 or total_blocks < 900:
  93. print(file)
  94. print("Error: Has {0} blocks".format(total_blocks))
  95. # 2b. Does each block have the correct CRC and fields?
  96. if total_blocks != 0:
  97. lat_min = 90 * 1.0e7
  98. lat_max = -90 * 1.0e7
  99. lon_min = 180 * 1.0e7
  100. lon_max = -180 * 1.0e7
  101. for blocknum in range(total_blocks):
  102. block = tile[(blocknum * IO_BLOCK_SIZE):((blocknum + 1)* IO_BLOCK_SIZE)-227]
  103. ret = check_filled(block, lat_int, lon_int, 100)
  104. if not ret:
  105. print(file)
  106. print("Bad data in block {0} of {1}".format(blocknum, total_blocks))
  107. else:
  108. (lat, lon) = ret
  109. lat_min = min(lat_min, lat)
  110. lat_max = max(lat_max, lat)
  111. lon_min = min(lon_min, lon)
  112. lon_max = max(lon_max, lon)
  113. lat_min *= 1.0e-7
  114. lat_max *= 1.0e-7
  115. lon_min *= 1.0e-7
  116. lon_max *= 1.0e-7
  117. if abs(lat_max-lat_min) < 0.99 or abs(lon_max-lon_min) < 1.00 or abs(lat_max-lat_min) > 1.01 or abs(lon_max-lon_min) > 1.07:
  118. print(file)
  119. print("Bad tile")
  120. print("Tile covers ({0},{1}) to ({2},{3})".format(lat_min, lon_min, lat_max, lon_max))
  121. print("Tile size is ({0:.4f}, {1:.4f}) degrees".format(lat_max-lat_min, lon_max-lon_min))
  122. else:
  123. print("Bad tile: " + file)
  124. print("Done!")