offline_check.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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. print("Has {0} blocks".format(total_blocks))
  93. # 2b. Does each block have the correct CRC and fields?
  94. if total_blocks != 0:
  95. lat_min = 90 * 1.0e7
  96. lat_max = -90 * 1.0e7
  97. lon_min = 180 * 1.0e7
  98. lon_max = -180 * 1.0e7
  99. for blocknum in range(total_blocks):
  100. block = tile[(blocknum * IO_BLOCK_SIZE):((blocknum + 1)* IO_BLOCK_SIZE)-227]
  101. ret = check_filled(block, lat_int, lon_int, 100)
  102. if not ret:
  103. print("Bad data in block {0} of {1}".format(blocknum, total_blocks))
  104. else:
  105. (lat, lon) = ret
  106. lat_min = min(lat_min, lat)
  107. lat_max = max(lat_max, lat)
  108. lon_min = min(lon_min, lon)
  109. lon_max = max(lon_max, lon)
  110. lat_min *= 1.0e-7
  111. lat_max *= 1.0e-7
  112. lon_min *= 1.0e-7
  113. lon_max *= 1.0e-7
  114. print("Tile covers ({0},{1}) to ({2},{3})".format(lat_min, lon_min, lat_max, lon_max))
  115. print("Tile size is ({0:.4f}, {1:.4f}) degrees".format(lat_max-lat_min, lon_max-lon_min))
  116. else:
  117. print("Bad tile: " + file)
  118. print("Done!")