Spaces:
Runtime error
Runtime error
File size: 58,880 Bytes
523a361 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 |
# -*- coding: utf-8 -*-
"""
===================================================
A python interface for ARIS files
===================================================
Last modified on: January 31, 2017
The most recent version can be found at: https://github.com/EminentCodfish/pyARIS
@author: Chris Rillahan
"""
import struct, array, pytz, datetime, tqdm
import os
import subprocess as sp
from matplotlib import cm as colormap
from PIL import Image, ImageFont, ImageDraw
import numpy as np
from beams import load_beam_width_data
class ARIS_File:
'This is a class container for the ARIS file headers'
def __init__(self, filename, version_number, FrameCount, FrameRate, HighResolution, NumRawBeams, SampleRate, SamplesPerChannel, ReceiverGain,
WindowStart, WindowLength, Reverse, SN, strDate, strHeaderID, UserID1, UserID2, UserID3, UserID4, StartFrame,EndFrame,
TimeLapse, RecordInterval, RadioSeconds, FrameInterval, Flags, AuxFlags, Sspd, Flags3D, SoftwareVersion, WaterTemp,
Salinity, PulseLength, TxMode, VersionFGPA, VersionPSuC, ThumbnailFI, FileSize, OptionalHeaderSize, OptionalTailSize,
VersionMinor, LargeLens):
self.filename = filename #Name of the ARIS file
self.version_number = version_number #File format version DDF_05 = 0x05464444
#OBSOLETE: Calculate the number of frames from file size & beams*samples.
self.FrameCount = FrameCount #Total frames in file
#OBSOLETE: See frame header instead.
self.FrameRate = FrameRate #Initial recorded frame rate
#OBSOLETE: See frame header instead.
self.HighResolution = HighResolution #Non-zero if HF, zero if LF
#OBSOLETE: See frame header instead.
self.NumRawBeams = NumRawBeams #ARIS 3000 = 128/64, ARIS 1800 = 96/48, ARIS 1200 = 48
#OBSOLETE: See frame header instead.
self.SampleRate = SampleRate #1/Sample Period
#OBSOLETE: See frame header instead.
self.SamplesPerChannel = SamplesPerChannel #Number of range samples in each beam
#OBSOLETE: See frame header instead.
self.ReceiverGain = ReceiverGain #Relative gain in dB: 0 - 40
#OBSOLETE: See frame header instead.
self.WindowStart = WindowStart #Image window start range in meters (code [0..31] in DIDSON)
#OBSOLETE: See frame header instead.
self.WindowLength = WindowLength #Image window length in meters (code [0..3] in DIDSON)
#OBSOLETE: See frame header instead.
self.Reverse = Reverse #Non-zero = lens down (DIDSON) or lens up (ARIS), zero = opposite
self.SN = SN #Sonar serial number
self.strDate = strDate #Date that file was recorded
self.strHeaderID = strHeaderID #User input to identify file in 256 characters
self.UserID1 = UserID1 #User-defined integer quantity
self.UserID2 = UserID2 #User-defined integer quantity
self.UserID3 = UserID3 #User-defined integer quantity
self.UserID4 = UserID4 #User-defined integer quantity
self.StartFrame = StartFrame #First frame number from source file (for DIDSON snippet files)
self.EndFrame = EndFrame #Last frame number from source file (for DIDSON snippet files)
self.TimeLapse = TimeLapse #Non-zero indicates time lapse recording
self.RecordInterval = RecordInterval #Number of frames/seconds between recorded frames
self.RadioSeconds = RadioSeconds #Frames or seconds interval
self.FrameInterval = FrameInterval #Record every Nth frame
self.Flags = Flags #See DDF_04 file format document (OBSOLETE)
self.AuxFlags = AuxFlags #See DDF_04 file format document
#OBSOLETE: See frame header instead.
self.Sspd = Sspd #Sound velocity in water
self.Flags3D = Flags3D #See DDF_04 file format document
self.SoftwareVersion = SoftwareVersion #DIDSON software version that recorded the file
self.WaterTemp = WaterTemp #Water temperature code: 0 = 5-15C, 1 = 15-25C, 2 = 25-35C
self.Salinity = Salinity #Salinity code: 0 = fresh, 1 = brackish, 2 = salt
self.PulseLength = PulseLength #Added for ARIS but not used
self.TxMode = TxMode #Added for ARIS but not used
self.VersionFGPA = VersionFGPA #Reserved for future use
self.VersionPSuC = VersionPSuC #Reserved for future use
self.ThumbnailFI = ThumbnailFI #Frame index of frame used for thumbnail image of file
#OBSOLETE: Do not use; query your filesystem instead.
self.FileSize = FileSize #Total file size in bytes
self.OptionalHeaderSize = OptionalHeaderSize#Reserved for future use (Obsolete, not used)
self.OptionalTailSize = OptionalTailSize #Reserved for future use (Obsolete, not used)
self.VersionMinor = VersionMinor #DIDSON_ADJUSTED_VERSION_MINOR (Obsolete)
self.LargeLens = LargeLens #Non-zero if telephoto lens (large lens, hi-res lens, big lens) is present
def __len__(self):
return self.FrameCount
def __repr__(self):
return 'ARIS File: ' + self.filename
def info(self):
print('Filename: ' + str(self.filename))
print('Software Version: ' + str(self.SoftwareVersion))
print('ARIS S/N: ' + str(self.SN))
print('File size: ' + str(self.FileSize))
print('Number of Frames: ' + str(self.FrameCount))
print('Beam Count: ' + str(self.NumRawBeams))
print('Samples/Beam: ' + str(self.SamplesPerChannel))
class ARIS_Frame(ARIS_File):
"""This is a class container for the ARIS frame dataPI"""
def __init__(self, frameindex, frametime, version, status, sonartimestamp, tsday, tshour, tsminute, tssecond, tshsecond, transmitmode,
windowstart, windowlength, threshold, intensity, receivergain, degc1, degc2, humidity, focus, battery, uservalue1, uservalue2,
uservalue3, uservalue4, uservalue5, uservalue6, uservalue7, uservalue8, velocity, depth, altitude, pitch, pitchrate, roll,
rollrate, heading, headingrate, compassheading, compasspitch, compassroll, latitude, longitude, sonarposition, configflags,
beamtilt, targetrange, targetbearing, targetpresent, firmwarerevision, flags, sourceframe, watertemp, timerperiod, sonarx,
sonary, sonarz, sonarpan, sonartilt, sonarroll, panpnnl, tiltpnnl, rollpnnl, vehicletime, timeggk, dateggk, qualityggk, numsatsggk,
dopggk, ehtggk, heavetss, yeargps, monthgps, daygps, hourgps, minutegps, secondgps, hsecondgps, sonarpanoffset, sonartiltoffset,
sonarrolloffset, sonarxoffset, sonaryoffset, sonarzoffset, tmatrix, samplerate, accellx, accelly, accellz, pingmode, frequencyhilow,
pulsewidth, cycleperiod, sampleperiod, transmitenable, framerate, soundspeed, samplesperbeam, enable150v, samplestartdelay, largelens,
thesystemtype, sonarserialnumber, encryptedkey, ariserrorflagsuint, missedpackets, arisappversion, available2, reorderedsamples,
salinity, pressure, batteryvoltage, mainvoltage, switchvoltage, focusmotormoving, voltagechanging, focustimeoutfault, focusovercurrentfault,
focusnotfoundfault, focusstalledfault, fpgatimeoutfault, fpgabusyfault, fpgastuckfault, cputempfault, psutempfault, watertempfault,
humidityfault, pressurefault, voltagereadfault, voltagewritefault, focuscurrentposition, targetpan, targettilt, targetroll, panmotorerrorcode,
tiltmotorerrorcode, rollmotorerrorcode, panabsposition, tiltabsposition, rollabsposition, panaccelx, panaccely, panaccelz, tiltaccelx,
tiltaccely, tiltaccelz, rollaccelx, rollaccely, rollaccelz, appliedsettings, constrainedsettings, invalidsettings, enableinterpacketdelay,
interpacketdelayperiod, uptime, arisappversionmajor, arisappversionminor, gotime, panvelocity, tiltvelocity, rollvelocity, sentinel):
self.frameindex = frameindex #Frame number in file
self.frametime = frametime #PC time stamp when recorded; microseconds since epoch (Jan 1st 1970)
self.version = version #ARIS file format version = 0x05464444
self.status = status
self.sonartimestamp = sonartimestamp #On-sonar microseconds since epoch (Jan 1st 1970)
self.tsday = tsday
self.tshour = tshour
self.tsminute = tsminute
self.tssecond = tssecond
self.tshsecond = tshsecond
self.transmitmode = transmitmode
self.windowstart = windowstart #Window start in meters
self.windowlength = windowlength #Window length in meters
self.threshold = threshold
self.intensity = intensity
self.receivergain = receivergain #Note: 0-24 dB
self.degc1 = degc1 #CPU temperature (C)
self.degc2 = degc2 #Power supply temperature (C)
self.humidity = humidity #% relative humidity
self.focus = focus #Focus units 0-1000
self.battery = battery #OBSOLETE: Unused.
self.uservalue1 = uservalue1
self.uservalue2 = uservalue2
self.uservalue3 = uservalue3
self.uservalue4 = uservalue4
self.uservalue5 = uservalue5
self.uservalue6 = uservalue6
self.uservalue7 = uservalue7
self.uservalue8 = uservalue8
self.velocity = velocity # Platform velocity from AUV integration
self.depth = depth # Platform depth from AUV integration
self.altitude = altitude # Platform altitude from AUV integration
self.pitch = pitch # Platform pitch from AUV integration
self.pitchrate = pitchrate # Platform pitch rate from AUV integration
self.roll = roll # Platform roll from AUV integration
self.rollrate = rollrate # Platform roll rate from AUV integration
self.heading = heading # Platform heading from AUV integration
self.headingrate = headingrate # Platform heading rate from AUV integration
self.compassheading = compassheading # Sonar compass heading output
self.compasspitch = compasspitch # Sonar compass pitch output
self.compassroll = compassroll # Sonar compass roll output
self.latitude = latitude # from auxiliary GPS sensor
self.longitude = longitude # from auxiliary GPS sensor
self.sonarposition = sonarposition # special for PNNL
self.configflags = configflags
self.beamtilt = beamtilt
self.targetrange = targetrange
self.targetbearing = targetbearing
self.targetpresent = targetpresent
self.firmwarerevision = firmwarerevision #OBSOLETE: Unused.
self.flags = flags
self.sourceframe = sourceframe # Source file frame number for CSOT output files
self.watertemp = watertemp # Water temperature from housing temperature sensor
self.timerperiod = timerperiod
self.sonarx = sonarx # Sonar X location for 3D processing
self.sonary = sonary # Sonar Y location for 3D processing
self.sonayz = sonarz # Sonar Z location for 3D processing
self.sonarpan = sonarpan # X2 pan output
self.sonartilt = sonartilt # X2 tilt output
self.sonarroll = sonarroll # X2 roll output **** End of DDF_03 frame header data ****
self.panpnnl = panpnnl
self.tiltpnnl = tiltpnnl
self.rollpnnl = rollpnnl
self.vehicletime = vehicletime # special for Bluefin Robotics HAUV or other AUV integration
self.timeggk = timeggk # GPS output from NMEA GGK message
self.dateggk = dateggk # GPS output from NMEA GGK message
self.qualityggk = qualityggk # GPS output from NMEA GGK message
self.numsatsggk = numsatsggk # GPS output from NMEA GGK message
self.dopggk = dopggk # GPS output from NMEA GGK message
self.ehtggk = ehtggk # GPS output from NMEA GGK message
self.heavetss = heavetss # external sensor
self.yeargps = yeargps # GPS year output
self.monthgps = monthgps # GPS month output
self.daygps = daygps # GPS day output
self.hourgps = hourgps # GPS hour output
self.minutegps = minutegps # GPS minute output
self.secondgps = secondgps # GPS second output
self.hsecondgps = hsecondgps # GPS 1/100th second output
self.sonarpanoffset = sonarpanoffset # Sonar mount location pan offset for 3D processing
self.sonartiltoffset = sonartiltoffset # Sonar mount location tilt offset for 3D processing
self.sonarrolloffset = sonarrolloffset # Sonar mount location roll offset for 3D processing
self.sonarxoffset = sonarxoffset # Sonar mount location X offset for 3D processing
self.sonaryoffset = sonaryoffset # Sonar mount location Y offset for 3D processing
self.sonarzoffset = sonarzoffset # Sonar mount location Z offset for 3D processing
self.tmatirx = tmatrix # 3D processing transformation matrix
self.samplerate = samplerate # Calculated as 1e6/SamplePeriod
self.accellx = accellx # X-axis sonar acceleration
self.accelly = accelly # Y-axis sonar acceleration
self.accellz = accellz # Z-axis sonar acceleration
self.pingmode = pingmode # ARIS ping mode [1..12]
self.frequencyhilow = frequencyhilow # 1 = HF, 0 = LF
self.pulsewidth = pulsewidth # Width of transmit pulse in usec, [4..100]
self.cycleperiod = cycleperiod # Ping cycle time in usec, [1802..65535]
self.sampleperiod = sampleperiod # Downrange sample rate in usec, [4..100]
self.tranmitenable = transmitenable # 1 = Transmit ON, 0 = Transmit OFF
self.framerate = framerate # Instantaneous frame rate between frame N and frame N-1
self.soundspeed = soundspeed # Sound velocity in water calculated from water temperature and salinity setting
self.samplesperbeam = samplesperbeam # Number of downrange samples in each beam
self.enable150v = enable150v # 1 = 150V ON (Max Power), 0 = 150V OFF (Min Power, 12V)
self.samplestartdelay = samplestartdelay # Delay from transmit until start of sampling (window start) in usec, [930..65535]
self.largelens = largelens # 1 = telephoto lens (large lens, big lens, hi-res lens) present
self.thesystemtype = thesystemtype # 1 = ARIS 3000, 0 = ARIS 1800, 2 = ARIS 1200
self.sonarserialnumber = sonarserialnumber # Sonar serial number as labeled on housing
self.encryptedkey = encryptedkey # Reserved for future use
self.ariserrorflagsuint = ariserrorflagsuint # Error flag code bits
self.missedpackets = missedpackets # Missed packet count for Ethernet statistics reporting
self.arisappversion = arisappversion # Version number of ArisApp sending frame data
self.available2 = available2 # Reserved for future use
self.reorderedsamples = reorderedsamples # 1 = frame data already ordered into [beam,sample] array, 0 = needs reordering
self.salinity = salinity # Water salinity code: 0 = fresh, 15 = brackish, 35 = salt
self.pressure = pressure # Depth sensor output in meters (psi)
self.batteryvoltage = batteryvoltage # Battery input voltage before power steering
self.mainvoltage = mainvoltage # Main cable input voltage before power steering
self.switchvoltage = switchvoltage # Input voltage after power steering
self.focusmotormoving = focusmotormoving # Added 14-Aug-2012 for AutomaticRecording
self.voltagechanging = voltagechanging # Added 16-Aug (first two bits = 12V, second two bits = 150V, 00 = not changing, 01 = turning on, 10 = turning off)
self.focustimeoutfault = focustimeoutfault
self.focusovercurrentfault = focusovercurrentfault
self.focusnotfoundfault = focusnotfoundfault
self.focusstalledfault = focusstalledfault
self.fpgatimeoutfault = fpgatimeoutfault
self.fpgabusyfault = fpgabusyfault
self.fpgastuckfault = fpgastuckfault
self.cputempfault = cputempfault
self.psutempfault = psutempfault
self.watertempfault = watertempfault
self.humidityfault = humidityfault
self.pressurefault = pressurefault
self.voltagereadfault = voltagereadfault
self.voltagewritefault = voltagewritefault
self.focuscurrentposition = focuscurrentposition # Focus shaft current position in motor units [0.1000]
self.targetpan = targetpan # Commanded pan position
self.targettilt = targettilt # Commanded tilt position
self.targetroll = targetroll # Commanded roll position
self.panmotorerrorcode = panmotorerrorcode
self.tiltmotorerrorcode = tiltmotorerrorcode
self.rollmotorerrorcode = rollmotorerrorcode
self.panabsposition = panabsposition # Low-resolution magnetic encoder absolute pan position
self.tiltabsposition = tiltabsposition # Low-resolution magnetic encoder absolute tilt position
self.rollabsposition = rollabsposition # Low-resolution magnetic encoder absolute roll position
self.panaccelx = panaccelx # Accelerometer outputs from AR2 CPU board sensor
self.panaccely = panaccely
self.panaccelz = panaccelz
self.tiltaccelx = tiltaccelx
self.tiltaccely = tiltaccely
self.tiltaccelz = tiltaccelz
self.rollaccelx = rollaccelx
self.rollaccely = rollaccely
self.rollccelz = rollaccelz
self.appliedsettings = appliedsettings # Cookie indices for command acknowlege in frame header
self.constrainedsettings = constrainedsettings
self.invalidsettings = invalidsettings
self.enableinterpacketdelay = enableinterpacketdelay # If true delay is added between sending out image data packets
self.interpacketdelayperiod = interpacketdelayperiod # packet delay factor in us (does not include function overhead time)
self.uptime = uptime # Total number of seconds sonar has been running
self.arisappverionmajor = arisappversionmajor # Major version number
self.arisappversionminor = arisappversionminor # Minor version number
self.gotime = gotime # Sonar time when frame cycle is initiated in hardware
self.panvelocity = panvelocity # AR2 pan velocity in degrees/second
self.tiltvelocity = tiltvelocity # AR2 tilt velocity in degrees/second
self.rollvelocity = rollvelocity # AR2 roll velocity in degrees/second
self.sentinel = sentinel # Used to measure the frame header size
def __repr__(self):
return 'ARIS Frame Number: ' + str(self.frameindex)
def info(self):
print('Frame Number: ' + str(self.frameindex))
print('Frame Time: ' + str(datetime.datetime.fromtimestamp(self.sonartimestamp/1000000, pytz.timezone('UTC')).strftime('%Y-%m-%d %H:%M:%S.%f')))
print('Frame Rate: ' + str(self.framerate))
print('Window Start: ' + str(self.windowstart))
print('Window Length: ' + str(self.windowlength))
print('Ping Mode: ' + str(self.pingmode))
print('Frequency: ' + str(self.frequencyhilow))
def DataImport(filename, startFrame = 1, frameBuffer = 0):
"""DataImport reads in the file specified by the filename. The function populates
a ARIS_File data structure. This function then calls the FrameRead() method
to load a starting frame.
Parameters
-----------
filename : Input file (*.aris)
startFrame : The first frame to be populated into the data structure
frameBuffer : This parameter is passed into the FrameRead method. It adds a
specified number of pixels around the edges of the remapped frame.
Returns
-------
output_data : a ARIS_File data structure
frame : An ARIS_Frame data structure
Notes
-------
Basic frame attributes can be found by calling the file.info() method.
A list of all the frames attributes can be found by using dir(file), some
of these may or may not be used by the ARIS.
"""
try:
data = open(filename, 'rb')
except:
print('File Error: An error occurred trying to read the file.')
raise
#Start reading file header
version_number = struct.unpack('I', data.read(4))[0]
FrameCount = struct.unpack('I', data.read(4))[0]
FrameRate = struct.unpack('I', data.read(4))[0]
HighResolution = struct.unpack('I', data.read(4))[0]
NumRawBeams = struct.unpack('I', data.read(4))[0]
SampleRate = struct.unpack('f', data.read(4))[0]
SamplesPerChannel = struct.unpack('I', data.read(4))[0]
ReceiverGain = struct.unpack('I', data.read(4))[0]
WindowStart = struct.unpack('f', data.read(4))[0]
WindowLength = struct.unpack('f', data.read(4))[0]
Reverse = struct.unpack('I', data.read(4))[0]
SN = struct.unpack('I', data.read(4))[0]
strDate = struct.unpack('32s', data.read(32))[0]
strHeaderID = struct.unpack('256s', data.read(256))[0]
UserID1 = struct.unpack('i', data.read(4))[0]
UserID2 = struct.unpack('i', data.read(4))[0]
UserID3 = struct.unpack('i', data.read(4))[0]
UserID4 = struct.unpack('i', data.read(4))[0]
StartFrame = struct.unpack('I', data.read(4))[0]
EndFrame = struct.unpack('I', data.read(4))[0]
TimeLapse = struct.unpack('I', data.read(4))[0]
RecordInterval = struct.unpack('I', data.read(4))[0]
RadioSeconds = struct.unpack('I', data.read(4))[0]
FrameInterval = struct.unpack('I', data.read(4))[0]
Flags = struct.unpack('I', data.read(4))[0]
AuxFlags = struct.unpack('I', data.read(4))[0]
Sspd = struct.unpack('I', data.read(4))[0]
Flags3D = struct.unpack('I', data.read(4))[0]
SoftwareVersion = struct.unpack('I', data.read(4))[0]
WaterTemp = struct.unpack('I', data.read(4))[0]
Salinity = struct.unpack('I', data.read(4))[0]
PulseLength = struct.unpack('I', data.read(4))[0]
TxMode = struct.unpack('I', data.read(4))[0]
VersionFGPA = struct.unpack('I', data.read(4))[0]
VersionPSuC = struct.unpack('I', data.read(4))[0]
ThumbnailFI = struct.unpack('I', data.read(4))[0]
FileSize = struct.unpack('Q', data.read(8))[0]
OptionalHeaderSize = struct.unpack('Q', data.read(8))[0]
OptionalTailSize = struct.unpack('Q', data.read(8))[0]
VersionMinor = struct.unpack('I', data.read(4))[0]
LargeLens = struct.unpack('I', data.read(4))[0]
#Create data structure
output_data = ARIS_File(filename, version_number, FrameCount, FrameRate, HighResolution, NumRawBeams, SampleRate, SamplesPerChannel, ReceiverGain,
WindowStart, WindowLength, Reverse, SN, strDate, strHeaderID, UserID1, UserID2, UserID3, UserID4, StartFrame,EndFrame,
TimeLapse, RecordInterval, RadioSeconds, FrameInterval, Flags, AuxFlags, Sspd, Flags3D, SoftwareVersion, WaterTemp,
Salinity, PulseLength, TxMode, VersionFGPA, VersionPSuC, ThumbnailFI, FileSize, OptionalHeaderSize, OptionalTailSize,
VersionMinor, LargeLens)
#Close data file
data.close()
#Create an empty container for the lookup table
output_data.LUP = None
#Load the first frame
frame = FrameRead(output_data, startFrame)
#Return the data structure
return output_data, frame
def FrameRead(ARIS_data, frameIndex, frameBuffer = None):
"""The FrameRead function loads in the specified frame data from the raw ARIS data.
The function then calls the remapARIS() function which remaps the raw data into
a 2D real world projection.
Parameters
-----------
ARIS_data : ARIS data structure returned via pyARIS.DataImport()
frameIndex : frame number
frameBuffer : This parameter add a specified number of pixels around the edges
of the remapped frame.
Returns
-------
output : a frame data structure
Notes
-------
Basic frame attributes can be found by calling the frame.info() method.
A list of all the frames attributes can be found by using dir(frame), some
of these may or may not be used by the ARIS.
"""
FrameSize = ARIS_data.NumRawBeams*ARIS_data.SamplesPerChannel
frameoffset = (1024+(frameIndex*(1024+(FrameSize))))
data = open(ARIS_data.filename, 'rb')
data.seek(frameoffset, 0)
frameindex = struct.unpack('I', data.read(4))[0] #Frame number in file
frametime = struct.unpack('Q', data.read(8))[0] #PC time stamp when recorded; microseconds since epoch (Jan 1st 1970)
version = struct.unpack('I', data.read(4))[0] #ARIS file format version = 0x05464444
status = struct.unpack('I', data.read(4))[0]
sonartimestamp = struct.unpack('Q', data.read(8))[0] #On-sonar microseconds since epoch (Jan 1st 1970)
tsday = struct.unpack('I', data.read(4))[0]
tshour = struct.unpack('I', data.read(4))[0]
tsminute = struct.unpack('I', data.read(4))[0]
tssecond = struct.unpack('I', data.read(4))[0]
tshsecond = struct.unpack('I', data.read(4))[0]
transmitmode = struct.unpack('I', data.read(4))[0]
windowstart = struct.unpack('f', data.read(4))[0] #Window start in meters
windowlength = struct.unpack('f', data.read(4))[0] #Window length in meters
threshold = struct.unpack('I', data.read(4))[0]
intensity = struct.unpack('i', data.read(4))[0]
receivergain = struct.unpack('I', data.read(4))[0] #Note: 0-24 dB
degc1 = struct.unpack('I', data.read(4))[0] #CPU temperature (C)
degc2 = struct.unpack('I', data.read(4))[0] #Power supply temperature (C)
humidity = struct.unpack('I', data.read(4))[0] #% relative humidity
focus = struct.unpack('I', data.read(4))[0] #Focus units 0-1000
battery = struct.unpack('I', data.read(4))[0] #OBSOLETE: Unused.
uservalue1 = struct.unpack('f', data.read(4))[0]
uservalue2 = struct.unpack('f', data.read(4))[0]
uservalue3 = struct.unpack('f', data.read(4))[0]
uservalue4 = struct.unpack('f', data.read(4))[0]
uservalue5 = struct.unpack('f', data.read(4))[0]
uservalue6 = struct.unpack('f', data.read(4))[0]
uservalue7 = struct.unpack('f', data.read(4))[0]
uservalue8 = struct.unpack('f', data.read(4))[0]
velocity = struct.unpack('f', data.read(4))[0] # Platform velocity from AUV integration
depth = struct.unpack('f', data.read(4))[0] # Platform depth from AUV integration
altitude = struct.unpack('f', data.read(4))[0] # Platform altitude from AUV integration
pitch = struct.unpack('f', data.read(4))[0] # Platform pitch from AUV integration
pitchrate = struct.unpack('f', data.read(4))[0] # Platform pitch rate from AUV integration
roll = struct.unpack('f', data.read(4))[0] # Platform roll from AUV integration
rollrate = struct.unpack('f', data.read(4))[0] # Platform roll rate from AUV integration
heading = struct.unpack('f', data.read(4))[0] # Platform heading from AUV integration
headingrate = struct.unpack('f', data.read(4))[0] # Platform heading rate from AUV integration
compassheading = struct.unpack('f', data.read(4))[0] # Sonar compass heading output
compasspitch = struct.unpack('f', data.read(4))[0] # Sonar compass pitch output
compassroll = struct.unpack('f', data.read(4))[0] # Sonar compass roll output
latitude = struct.unpack('d', data.read(8))[0] # from auxiliary GPS sensor
longitude = struct.unpack('d', data.read(8))[0] # from auxiliary GPS sensor
sonarposition = struct.unpack('f', data.read(4))[0] # special for PNNL
configflags = struct.unpack('I', data.read(4))[0]
beamtilt = struct.unpack('f', data.read(4))[0]
targetrange = struct.unpack('f', data.read(4))[0]
targetbearing = struct.unpack('f', data.read(4))[0]
targetpresent = struct.unpack('I', data.read(4))[0]
firmwarerevision = struct.unpack('I', data.read(4))[0] #OBSOLETE: Unused.
flags = struct.unpack('I', data.read(4))[0]
sourceframe = struct.unpack('I', data.read(4))[0] # Source file frame number for CSOT output files
watertemp = struct.unpack('f', data.read(4))[0] # Water temperature from housing temperature sensor
timerperiod = struct.unpack('I', data.read(4))[0]
sonarx = struct.unpack('f', data.read(4))[0] # Sonar X location for 3D processing
sonary = struct.unpack('f', data.read(4))[0] # Sonar Y location for 3D processing
sonarz = struct.unpack('f', data.read(4))[0] # Sonar Z location for 3D processing
sonarpan = struct.unpack('f', data.read(4))[0] # X2 pan output
sonartilt = struct.unpack('f', data.read(4))[0] # X2 tilt output
sonarroll = struct.unpack('f', data.read(4))[0] # X2 roll output **** End of DDF_03 frame header data ****
panpnnl = struct.unpack('f', data.read(4))[0]
tiltpnnl = struct.unpack('f', data.read(4))[0]
rollpnnl = struct.unpack('f', data.read(4))[0]
vehicletime = struct.unpack('d', data.read(8))[0] # special for Bluefin Robotics HAUV or other AUV integration
timeggk = struct.unpack('f', data.read(4))[0] # GPS output from NMEA GGK message
dateggk = struct.unpack('I', data.read(4))[0] # GPS output from NMEA GGK message
qualityggk = struct.unpack('I', data.read(4))[0] # GPS output from NMEA GGK message
numsatsggk = struct.unpack('I', data.read(4))[0] # GPS output from NMEA GGK message
dopggk = struct.unpack('f', data.read(4))[0] # GPS output from NMEA GGK message
ehtggk = struct.unpack('f', data.read(4))[0] # GPS output from NMEA GGK message
heavetss = struct.unpack('f', data.read(4))[0] # external sensor
yeargps = struct.unpack('I', data.read(4))[0] # GPS year output
monthgps = struct.unpack('I', data.read(4))[0] # GPS month output
daygps = struct.unpack('I', data.read(4))[0] # GPS day output
hourgps = struct.unpack('I', data.read(4))[0] # GPS hour output
minutegps = struct.unpack('I', data.read(4))[0] # GPS minute output
secondgps = struct.unpack('I', data.read(4))[0] # GPS second output
hsecondgps = struct.unpack('I', data.read(4))[0] # GPS 1/100th second output
sonarpanoffset = struct.unpack('f', data.read(4))[0] # Sonar mount location pan offset for 3D processing
sonartiltoffset = struct.unpack('f', data.read(4))[0] # Sonar mount location tilt offset for 3D processing
sonarrolloffset = struct.unpack('f', data.read(4))[0] # Sonar mount location roll offset for 3D processing
sonarxoffset = struct.unpack('f', data.read(4))[0] # Sonar mount location X offset for 3D processing
sonaryoffset = struct.unpack('f', data.read(4))[0] # Sonar mount location Y offset for 3D processing
sonarzoffset = struct.unpack('f', data.read(4))[0] # Sonar mount location Z offset for 3D processing
tmatrix = array.array('f') # 3D processing transformation matrix
for i in range(16):
tmatrix.append(struct.unpack('f', data.read(4))[0])
samplerate = struct.unpack('f', data.read(4))[0] # Calculated as 1e6/SamplePeriod
accellx = struct.unpack('f', data.read(4))[0] # X-axis sonar acceleration
accelly = struct.unpack('f', data.read(4))[0] # Y-axis sonar acceleration
accellz = struct.unpack('f', data.read(4))[0] # Z-axis sonar acceleration
pingmode = struct.unpack('I', data.read(4))[0] # ARIS ping mode [1..12]
frequencyhilow = struct.unpack('I', data.read(4))[0] # 1 = HF, 0 = LF
pulsewidth = struct.unpack('I', data.read(4))[0] # Width of transmit pulse in usec, [4..100]
cycleperiod = struct.unpack('I', data.read(4))[0] # Ping cycle time in usec, [1802..65535]
sampleperiod = struct.unpack('I', data.read(4))[0] # Downrange sample rate in usec, [4..100]
transmitenable = struct.unpack('I', data.read(4))[0] # 1 = Transmit ON, 0 = Transmit OFF
framerate = struct.unpack('f', data.read(4))[0] # Instantaneous frame rate between frame N and frame N-1
soundspeed = struct.unpack('f', data.read(4))[0] # Sound velocity in water calculated from water temperature and salinity setting
samplesperbeam = struct.unpack('I', data.read(4))[0] # Number of downrange samples in each beam
enable150v = struct.unpack('I', data.read(4))[0] # 1 = 150V ON (Max Power), 0 = 150V OFF (Min Power, 12V)
samplestartdelay = struct.unpack('I', data.read(4))[0] # Delay from transmit until start of sampling (window start) in usec, [930..65535]
largelens = struct.unpack('I', data.read(4))[0] # 1 = telephoto lens (large lens, big lens, hi-res lens) present
thesystemtype = struct.unpack('I', data.read(4))[0] # 1 = ARIS 3000, 0 = ARIS 1800, 2 = ARIS 1200
sonarserialnumber = struct.unpack('I', data.read(4))[0] # Sonar serial number as labeled on housing
encryptedkey = struct.unpack('Q', data.read(8))[0] # Reserved for future use
ariserrorflagsuint = struct.unpack('I', data.read(4))[0] # Error flag code bits
missedpackets = struct.unpack('I', data.read(4))[0] # Missed packet count for Ethernet statistics reporting
arisappversion = struct.unpack('I', data.read(4))[0] # Version number of ArisApp sending frame data
available2 = struct.unpack('I', data.read(4))[0] # Reserved for future use
reorderedsamples = struct.unpack('I', data.read(4))[0] # 1 = frame data already ordered into [beam,sample] array, 0 = needs reordering
salinity = struct.unpack('I', data.read(4))[0] # Water salinity code: 0 = fresh, 15 = brackish, 35 = salt
pressure = struct.unpack('f', data.read(4))[0] # Depth sensor output in meters (psi)
batteryvoltage = struct.unpack('f', data.read(4))[0] # Battery input voltage before power steering
mainvoltage = struct.unpack('f', data.read(4))[0] # Main cable input voltage before power steering
switchvoltage = struct.unpack('f', data.read(4))[0] # Input voltage after power steering
focusmotormoving = struct.unpack('I', data.read(4))[0] # Added 14-Aug-2012 for AutomaticRecording
voltagechanging = struct.unpack('I', data.read(4))[0] # Added 16-Aug (first two bits = 12V, second two bits = 150V, 00 = not changing, 01 = turning on, 10 = turning off)
focustimeoutfault = struct.unpack('I', data.read(4))[0]
focusovercurrentfault = struct.unpack('I', data.read(4))[0]
focusnotfoundfault = struct.unpack('I', data.read(4))[0]
focusstalledfault = struct.unpack('I', data.read(4))[0]
fpgatimeoutfault = struct.unpack('I', data.read(4))[0]
fpgabusyfault = struct.unpack('I', data.read(4))[0]
fpgastuckfault = struct.unpack('I', data.read(4))[0]
cputempfault = struct.unpack('I', data.read(4))[0]
psutempfault = struct.unpack('I', data.read(4))[0]
watertempfault = struct.unpack('I', data.read(4))[0]
humidityfault = struct.unpack('I', data.read(4))[0]
pressurefault = struct.unpack('I', data.read(4))[0]
voltagereadfault = struct.unpack('I', data.read(4))[0]
voltagewritefault = struct.unpack('I', data.read(4))[0]
focuscurrentposition = struct.unpack('I', data.read(4))[0] # Focus shaft current position in motor units [0.1000]
targetpan = struct.unpack('f', data.read(4))[0] # Commanded pan position
targettilt = struct.unpack('f', data.read(4))[0] # Commanded tilt position
targetroll = struct.unpack('f', data.read(4))[0] # Commanded roll position
panmotorerrorcode = struct.unpack('I', data.read(4))[0]
tiltmotorerrorcode = struct.unpack('I', data.read(4))[0]
rollmotorerrorcode = struct.unpack('I', data.read(4))[0]
panabsposition = struct.unpack('f', data.read(4))[0] # Low-resolution magnetic encoder absolute pan position
tiltabsposition = struct.unpack('f', data.read(4))[0] # Low-resolution magnetic encoder absolute tilt position
rollabsposition = struct.unpack('f', data.read(4))[0] # Low-resolution magnetic encoder absolute roll position
panaccelx = struct.unpack('f', data.read(4))[0] # Accelerometer outputs from AR2 CPU board sensor
panaccely = struct.unpack('f', data.read(4))[0]
panaccelz = struct.unpack('f', data.read(4))[0]
tiltaccelx = struct.unpack('f', data.read(4))[0]
tiltaccely = struct.unpack('f', data.read(4))[0]
tiltaccelz = struct.unpack('f', data.read(4))[0]
rollaccelx = struct.unpack('f', data.read(4))[0]
rollaccely = struct.unpack('f', data.read(4))[0]
rollaccelz = struct.unpack('f', data.read(4))[0]
appliedsettings = struct.unpack('I', data.read(4))[0] # Cookie indices for command acknowlege in frame header
constrainedsettings = struct.unpack('I', data.read(4))[0]
invalidsettings = struct.unpack('I', data.read(4))[0]
enableinterpacketdelay = struct.unpack('I', data.read(4))[0] # If true delay is added between sending out image data packets
interpacketdelayperiod = struct.unpack('I', data.read(4))[0] # packet delay factor in us (does not include function overhead time)
uptime = struct.unpack('I', data.read(4))[0] # Total number of seconds sonar has been running
arisappversionmajor = struct.unpack('H', data.read(2))[0] # Major version number
arisappversionminor = struct.unpack('H', data.read(2))[0] # Minor version number
gotime = struct.unpack('Q', data.read(8))[0] # Sonar time when frame cycle is initiated in hardware
panvelocity = struct.unpack('f', data.read(4))[0] # AR2 pan velocity in degrees/second
tiltvelocity = struct.unpack('f', data.read(4))[0] # AR2 tilt velocity in degrees/second
rollvelocity = struct.unpack('f', data.read(4))[0] # AR2 roll velocity in degrees/second
sentinel = struct.unpack('I', data.read(4))[0] # Used to measure the frame header size
#Create the ARIS_frame data structure and add the meta-data
output = ARIS_Frame(frameindex, frametime, version, status, sonartimestamp, tsday, tshour, tsminute, tssecond, tshsecond, transmitmode,
windowstart, windowlength, threshold, intensity, receivergain, degc1, degc2, humidity, focus, battery, uservalue1, uservalue2,
uservalue3, uservalue4, uservalue5, uservalue6, uservalue7, uservalue8, velocity, depth, altitude, pitch, pitchrate, roll,
rollrate, heading, headingrate, compassheading, compasspitch, compassroll, latitude, longitude, sonarposition, configflags,
beamtilt, targetrange, targetbearing, targetpresent, firmwarerevision, flags, sourceframe, watertemp, timerperiod, sonarx,
sonary, sonarz, sonarpan, sonartilt, sonarroll, panpnnl, tiltpnnl, rollpnnl, vehicletime, timeggk, dateggk, qualityggk, numsatsggk,
dopggk, ehtggk, heavetss, yeargps, monthgps, daygps, hourgps, minutegps, secondgps, hsecondgps, sonarpanoffset, sonartiltoffset,
sonarrolloffset, sonarxoffset, sonaryoffset, sonarzoffset, tmatrix, samplerate, accellx, accelly, accellz, pingmode, frequencyhilow,
pulsewidth, cycleperiod, sampleperiod, transmitenable, framerate, soundspeed, samplesperbeam, enable150v, samplestartdelay, largelens,
thesystemtype, sonarserialnumber, encryptedkey, ariserrorflagsuint, missedpackets, arisappversion, available2, reorderedsamples,
salinity, pressure, batteryvoltage, mainvoltage, switchvoltage, focusmotormoving, voltagechanging, focustimeoutfault, focusovercurrentfault,
focusnotfoundfault, focusstalledfault, fpgatimeoutfault, fpgabusyfault, fpgastuckfault, cputempfault, psutempfault, watertempfault,
humidityfault, pressurefault, voltagereadfault, voltagewritefault, focuscurrentposition, targetpan, targettilt, targetroll, panmotorerrorcode,
tiltmotorerrorcode, rollmotorerrorcode, panabsposition, tiltabsposition, rollabsposition, panaccelx, panaccely, panaccelz, tiltaccelx,
tiltaccely, tiltaccelz, rollaccelx, rollaccely, rollaccelz, appliedsettings, constrainedsettings, invalidsettings, enableinterpacketdelay,
interpacketdelayperiod, uptime, arisappversionmajor, arisappversionminor, gotime, panvelocity, tiltvelocity, rollvelocity, sentinel)
#Add the frame data
if pingmode in [1,2]:
ARIS_Frame.BeamCount = 48
if pingmode in [3,4,5]:
ARIS_Frame.BeamCount = 96
if pingmode in [6,7,8]:
ARIS_Frame.BeamCount = 64
if pingmode in [9,10,11,12]:
ARIS_Frame.BeamCount = 128
data.seek(frameoffset+1024, 0)
frame = np.empty([samplesperbeam, ARIS_Frame.BeamCount], dtype=float)
for r in range(len(frame)):
for c in range(len(frame[r])):
frame[r][c] = struct.unpack('B', data.read(1))[0]
frame = np.fliplr(frame)
#Remap the data from 0-255 to 0-80 dB
#remap = lambda t: (t * 80)/255
#vfunc = np.vectorize(remap)
#frame = vfunc(frame)
output.frame_data = frame
output.WinStart = output.samplestartdelay * 0.000001 * output.soundspeed / 2
#Close the data file
data.close()
return output
def get_box_for_sample(beam_num, bin_num, frame, beam_data):
""" Get the box coordinates (in meters) for a sample.
This is a non-axis aligned box.
Returns:
back left, back right, front right, front left
"""
sample_start_delay = frame.samplestartdelay # usec
sound_speed = frame.soundspeed # meters / sec
sample_period = frame.sampleperiod # usec
WindowStart = sample_start_delay * 1e-6 * sound_speed / 2 # meters
sample_length = sample_period * 1e-6 * sound_speed / 2. # meters
bin_front_edge_distance = WindowStart + sample_length * bin_num
bin_back_edge_distance = WindowStart + sample_length* (bin_num + 1)
beam_angles = beam_data[beam_data['beam_num'] == beam_num]
a1 = beam_angles['beam_left'].iloc[0]
a2 = beam_angles['beam_right'].iloc[0]
c = beam_angles['beam_center'].iloc[0]
# I can't figure out whats going on with the beam spacing in the files.
# Once the center point crosses 0, the ordering of the left and right angles swap...
# For now I'll assume the y axis is the common line. Positive angles go to the left,
# negative angles go to the right
left = max(a1, a2)
right = min(a1, a2)
# Left Edge
beam_left_angle = np.deg2rad(left)
rot_matrix = np.array([[np.cos(beam_left_angle), -np.sin(beam_left_angle)], [np.sin(beam_left_angle), np.cos(beam_left_angle)]])
vec = np.array([0, bin_back_edge_distance])
bin_left_back_point = np.matmul(rot_matrix, vec)
vec = np.array([0, bin_front_edge_distance])
bin_left_front_point = np.matmul(rot_matrix, vec)
# Right Edge
beam_right_angle = np.deg2rad(right)
rot_matrix = np.array([[np.cos(beam_right_angle), -np.sin(beam_right_angle)], [np.sin(beam_right_angle), np.cos(beam_right_angle)]])
vec = np.array([0, bin_front_edge_distance])
bin_right_front_point = np.matmul(rot_matrix, vec)
vec = np.array([0, bin_back_edge_distance])
bin_right_back_point = np.matmul(rot_matrix, vec)
return bin_left_back_point, bin_right_back_point, bin_right_front_point, bin_left_front_point
def xy_to_sample(x, y, frame, beam_data):
""" Convert an x,y location (in meters) to a beam sample.
Returns:
beam num
bin num
"""
# Get the angle
angle = np.rad2deg(np.arctan(x / y))
beam_num = beam_data[(beam_data['beam_left'] <= angle) & (angle <= beam_data['beam_right'])]
if beam_num.shape[0] == 0:
return None, None
beam_num = beam_num['beam_num'].iloc[0]
# Get the distance
hyp = y / np.cos(np.arctan(x / y))
# Take into account the window start
hyp -= frame.WinStart
# Sample length
bin_length = frame.sampleperiod * 0.000001 * frame.soundspeed / 2.
# Convert to bins
bin_num = int(hyp / bin_length)
if bin_num < 0 or bin_num > (frame.BeamCount - 1):
return None, None
return beam_num, bin_num
def get_minimum_pixel_meter_size(frame, beam_width_data):
""" Compute the smallest pixel size that will bound a sample.
"""
all_widths = []
all_heights = []
for beam_num in range(frame.BeamCount):
bl, br, fr, fl = get_box_for_sample(beam_num, 0, frame, beam_width_data)
# determine the axis aligned box around this sample box.
xs = [bl[0], br[0], fr[0], fl[0]]
ys = [bl[1], br[1], fr[1], fl[1]]
min_x = min(xs)
max_x = max(xs)
min_y = min(ys)
max_y = max(ys)
width = max_x - min_x
height = max_y - min_y
all_widths.append(width)
all_heights.append(height)
min_width = min(all_widths)
min_height = min(all_heights)
return min(min_width, min_height)
def compute_image_bounds(pixel_meter_size, frame, beam_width_data, additional_pixel_padding_x=0, additional_pixel_padding_y=0):
""" Given the size of a pixel in meters, compute the bounds of an image that will contain the frame.
"""
# Compute the projected locations of all samples so that we can get the extent
all_bl = []
all_br = []
all_fr = []
all_fl = []
for beam_num in [0, frame.BeamCount / 2, frame.BeamCount - 1]:
for bin_num in [0, frame.samplesperbeam - 1]:
bl, br, fr, fl = get_box_for_sample(beam_num, bin_num, frame, beam_width_data)
all_bl.append(bl)
all_br.append(br)
all_fr.append(fr)
all_fl.append(fl)
all_bl = np.array(all_bl)
all_br = np.array(all_br)
all_fr = np.array(all_fr)
all_fl = np.array(all_fl)
# Get the xdim extent
min_back_left = np.min(all_bl[:,0])
min_back_right = np.min(all_br[:,0])
min_front_left = np.min(all_fl[:,0])
min_front_right = np.min(all_fr[:,0])
assert min_back_left < min_back_right
assert min_back_left < min_front_left
assert min_back_left < min_front_right
max_back_left = np.max(all_bl[:,0])
max_back_right = np.max(all_br[:,0])
max_front_left = np.max(all_fl[:,0])
max_front_right = np.max(all_fr[:,0])
assert max_back_right > max_back_left
assert max_back_right > max_front_left
assert max_back_right > max_front_right
xdim_extent = np.array([min_back_left, max_back_right])
# Get the ydim extent
min_back_left = np.min(all_bl[:,1])
min_back_right = np.min(all_br[:,1])
min_front_left = np.min(all_fl[:,1])
min_front_right = np.min(all_fr[:,1])
min_front = min(min_front_left, min_front_right)
assert min_front < min_back_right
assert min_front < min_back_left
max_back_left = np.max(all_bl[:,1])
max_back_right = np.max(all_br[:,1])
max_front_left = np.max(all_fl[:,1])
max_front_right = np.max(all_fr[:,1])
max_back = max(max_back_left, max_back_right)
assert max_back > max_front_right
assert max_back > max_front_left
ydim_extent = np.array([min_front, max_back])
# Determine which meter location corresponds to our "target center"
bl, br, fr, fl = get_box_for_sample(frame.BeamCount / 2, 0, frame, beam_width_data)
target_center_x = (fl[0] + fr[0]) / 2.
target_center_y = (bl[1] + fl[1]) / 2.
# Determine the x dimension size and what this corresponds to in meters
extra_padding_x = pixel_meter_size + pixel_meter_size * additional_pixel_padding_x
# X Min
xmin_len = target_center_x - xdim_extent[0]
xp = xmin_len % pixel_meter_size
xmin_padded = xdim_extent[0] - (extra_padding_x - xp)
xmin_len = target_center_x - xmin_padded
x_min_cells = np.abs(xmin_len / pixel_meter_size)
x_min_meters = target_center_x - xmin_len
assert x_min_meters <= xdim_extent[0]
# X Max
xmax_len = xdim_extent[1] - target_center_x
xp = xmax_len % pixel_meter_size
xmax_padded = xdim_extent[1] + (extra_padding_x - xp)
xmax_len = xmax_padded - target_center_x
x_max_cells = np.abs(xmax_len / pixel_meter_size)
x_max_meters = target_center_x + xmax_len
assert x_max_meters >= xdim_extent[1]
# if we want a specific beam to be the in the middle of the image then we should take the max?
xdim = int(x_min_cells + x_max_cells)
x_meter_start = x_min_meters
x_meter_stop = x_max_meters
# Determine the y dimension size and what this corresponds to in meters
extra_padding_y = pixel_meter_size + pixel_meter_size * additional_pixel_padding_y
# Y Min
ymin_len = target_center_y - ydim_extent[0]
yp = ymin_len % pixel_meter_size
ymin_padded = ydim_extent[0] - ( extra_padding_y - yp)
ymin_len = target_center_y - ymin_padded
y_min_cells = np.abs(ymin_len / pixel_meter_size)
y_min_meters = target_center_y - ymin_len
assert y_min_meters <= ydim_extent[0]
# Y Max
ymax_len = ydim_extent[1] - target_center_y
yp = ymax_len % pixel_meter_size
ymax_padded = ydim_extent[1] + (extra_padding_y - yp)
ymax_len = ymax_padded - target_center_y
y_max_cells = np.abs(ymax_len / pixel_meter_size)
y_max_meters = target_center_y + ymax_len
assert y_max_meters >= ydim_extent[1]
ydim = int(y_min_cells + y_max_cells)
y_meter_start = y_max_meters
y_meter_stop = y_min_meters
return xdim, ydim, x_meter_start, y_meter_start, x_meter_stop, y_meter_stop
def compute_mapping_from_sample_to_image(pixel_meter_size, xdim, ydim, x_meter_start, y_meter_start, frame, beam_width_data):
x_meter_values = np.array([x_meter_start + i * pixel_meter_size for i in range(xdim)])
y_meter_values = np.array([y_meter_start - i * pixel_meter_size for i in range(ydim)])
YY, XX = np.meshgrid(y_meter_values, x_meter_values, indexing='ij')
XYpairs = np.vstack([ XX.reshape(-1), YY.reshape(-1) ]).T
II, JJ = np.meshgrid(np.arange(ydim), np.arange(xdim), indexing='ij')
IJpairs = np.vstack([ II.reshape(-1), JJ.reshape(-1)]).T
# Get the angle of the xy pairs
angles = np.rad2deg(np.arctan(XYpairs[:,0] / XYpairs[:,1]))
# Discard pairs that have an angle that is out of range
min_angle = beam_width_data['beam_left'].min()
max_angle = beam_width_data['beam_right'].max()
valid_pairs = (angles > min_angle) & (angles < max_angle)
angles = angles[valid_pairs]
XYpairs = XYpairs[valid_pairs]
IJpairs = IJpairs[valid_pairs]
# Get the distance of the xy pairs
hyp = XYpairs[:,1] / np.cos(np.arctan(XYpairs[:,0] / XYpairs[:,1]))
# Take into account the window start
hyp -= frame.WinStart
# Sample length
bin_length = frame.sampleperiod * 0.000001 * frame.soundspeed / 2.
# Convert to bins
bin_nums = (hyp / bin_length).astype(int)
# Discard pairs that have a distance that is out of range
valid_pairs = (bin_nums >= 0) & (bin_nums < frame.samplesperbeam)
bin_nums = bin_nums[valid_pairs]
angles = angles[valid_pairs]
XYpairs = XYpairs[valid_pairs]
IJpairs = IJpairs[valid_pairs]
beam_edges = beam_width_data['beam_left'].to_numpy()
beam_nums = np.digitize(angles, beam_edges) - 1
# For each valid x,y pair compute which beam it falls into
write_to = [] # (i, j) of image
read_from = [] # (bin_num, beam_num) of frame data
for index in range(XYpairs.shape[0]):
bin_num = bin_nums[index]
beam_num = beam_nums[index]
write_to.append(IJpairs[index])
read_from.append((bin_num, beam_num))
read_from = np.array(read_from)
read_from_rows = np.array(read_from[:,0])
read_from_cols = np.array(read_from[:,1])
write_to = np.array(write_to)
write_to_rows = np.array(write_to[:,0])
write_to_cols = np.array(write_to[:,1])
return read_from_rows, read_from_cols, write_to_rows, write_to_cols
def make_video(data,
xdim, ydim, sample_read_rows, sample_read_cols, image_write_rows, image_write_cols,
directory, filename, fps = 24.0, start_frame = 1, end_frame = None, timestamp = False, fontsize = 30, ts_pos = (0,0), save_raw = False):
"""Output video using the ffmpeg pipeline. The current implementation
outputs compresses png files and outputs a mp4.
Parameters
-----------
data : (Str) ARIS data structure returned via pyARIS.DataImport()
filename : (Str) output filename. Must include file extension (i.e. 'video.mp4')
fps : (Float) Output video frame rate (frames per second). Default = 24 fps
start_frame, end_frame : (Int) Range of frames included in the output video
timestamp : (Bool) Add the timestamp from the sonar to the video frames
fontsize : (Int) Size of timestamp font
ts_pos : (Tuple) (x,y) location of the timestamp
Returns
-------
Returns a video into the current working directory
Notes
------
Currently this function looks for ffmpeg.exe in the current working directory.
Must have the '*.mp4' file extension.
Uses the tqdm package to display a status bar.
Example
-------
>>> pyARIS.VideoExport(data, 'test_video.mp4', fps = 24)
"""
#Command to send via the command prompt which specifies the pipe parameters
# command = ['ffmpeg',
# '-y', # (optional) overwrite output file if it exists
# '-f', 'image2pipe',
# '-vcodec', 'mjpeg', #'mjpeg',
# '-r', '1',
# '-r', str(fps), # frames per second
# '-i', '-', # The input comes from a pipe
# '-an', # Tells FFMPEG not to expect any audio
# '-vcodec', 'mpeg4',
# '-b:v', '5000k',
# directory + filename + "/"+filename+".mp4",
# '-hide_banner',
# '-loglevel', 'panic']
# Create directories if they don't exist
if not os.path.exists(os.path.join(directory, filename, 'frames/')):
os.makedirs(os.path.join(directory, filename, 'frames/'))
if save_raw and not os.path.exists(os.path.join(directory, filename, 'frames-raw/')):
os.makedirs(os.path.join(directory, filename, 'frames-raw/'))
if end_frame == None:
end_frame = data.FrameCount
cm = colormap.get_cmap('viridis')
for i, frame_offset in enumerate(tqdm.tqdm(range(start_frame, end_frame))):
frame = FrameRead(data, frame_offset)
frame_image = np.zeros([ydim, xdim], dtype=np.uint8)
frame_image[image_write_rows, image_write_cols] = frame.frame_data[sample_read_rows, sample_read_cols]
rgb_im = Image.fromarray(cm(frame_image, bytes=True)).convert('RGB')
rgb_im.save(os.path.join(directory, filename, 'frames/', f'{i}.jpg'), 'JPEG')
if save_raw:
Image.fromarray(np.uint8(frame.frame_data), mode='L').save(os.path.join(directory, filename, 'frames-raw/', f'{i}.jpg'), 'JPEG')
|