Sunday, December 8, 2019

Memory: Master Boot Record and Partition Tables

In summary, this post describes fundamentals of persistent memory storage, that is, how to interpret the data on most memory devices including SD cards, eMMC devices, flash and thumb drives, hard disk drives, and so on.

Master Boot Record

The master boot record (MBR) describes traits of the memory, bootloader (or bootstrap) code, and most especially the partition tables. A classic MBR is formatted as follows:

AddressDescriptionSize
(bytes)
HexDec
+000hex+0Bootstrap code area446
+1BEhex+446Partition entry №1Partition table
(for primary partitions)
16
+1CEhex+462Partition entry №216
+1DEhex+478Partition entry №316
+1EEhex+494Partition entry №416
+1FEhex+51055hexBoot signature[a]2
+1FFhex+511AAhex
Total size: 446 + 4×16 + 2512

The MBR is located in the first sector of the entire storage device - sector 1. Theoretically this is the equivalent to the memory address '0x0'. There are exactly four partition table entries, no more, no less for historical and compatibility reasons. The end of the boot record is signaled by the signature "0x55AA." MBR addresses are absolute, meaning address 0x000001BE is always going to be the address for partition table entry one.

Define 'Sector': An aligned chunk of data within a memory device. Sectors are usually no less than 512 bytes each. Larger memory devices sometimes use 1024 bytes up to 4096 bytes per sector. SD cards are commonly 512 bytes per sector.

Partition Table Entries

An entry describes where in the memory space each partition is located, how big it is, the partition type, and whether or not the partition is active. Side note: an active partition can be booted from, that is, an operating system or other bootstrap can execute code contained within the partition's own boot record. The byte-code layout of a partition is formatted as follows:

AddressDescriptionSize
(bits)
HexDec
+0hex+0Active Partition (0x00 - inactive, 0x80 - active, other - invalid)8
+1hex+1Start Head8
+2hex+2*Start Sector (bits 0-5)8
+3hex+3*Start Cylinder (bits 0-7 and bits 6&7 from the previous byte)8
+4hex+4Partition Type8
+5hex+5End Head8
+6hex+6*End Sector (bits 0-5)8
+7hex+7*End Cylinder (bits 0-7 and bits 6&7 from the previous byte)8
+8hex+8†Absolute starting sector of the partition8
+Chex+12†Size of the partition in sectors8
Total size: 16x8128

* Start sector and end sector values are only 6-bits in size. The remaining two bits in the byte are the most significant bits of the 10-bit start and end cylinder values respectively. For example, if the 'Start Sector' byte is 0xFE and the 'Start Cylinder' byte is 0xFF, then their actual values are 0x3E and 0x3FF respectively.
† Multi-byte values are little-endian! So an absolute starting sector with a value of 0x00080000 for example is actually 0x800. Same goes for the sector size.

An (incomplete) list of partition types:

Value (hex)Description
00Empty partition-table entry
01FAT12
04FAT16 (<= 32 MB)
05Extended Partition
06FAT16 (> 32 MB)
07OS/2 HPFS, Windows NT NTFS, Advanced Unix
08OS/2 v1.0-1.3, AIX Bootable Partition, SplitDrive
09AIX Data Partition
0AOS/2 Boot Manager
0BFAT32
0CFAT32 (using LBA-mode INT13 extensions)
0EFAT16 (> 32 MB using INT13 extensions)
0FExtended Partition (using INT13 extensions)
17Hidden NTFS
1BHidden FAT32
1CHidden FAT32 (using LBA-mode INT13 extensions)
1EHidden LBA VFAT
42Dynamic Disk Volume
50OnTrack Disk Manager (read-only)
51OnTrack Disk Manager (read-write)
81Linux
82Linux Swap, Solaris
83Linux Native (EXT2 / XIAFS)
85Linux EXT
86FAT16 Volume/Stripe (Windows NT)
87HPFS Fault-Tolerant Mirrored, NTFS Volume/Strip
BESolaris Boot
C0DR-DOS / Novell DOS Secured
C6Corrupt FAT16 Volume/Strip
C7Corrupt NTFS Volume/Stripe
F2Secondary Partition

Cylinder-Head-Sector Geomtery & Logical Block Addressing

Cylinder-Header-Sector (CHS) is the legacy way of locating the start and end sectors of partitions. Historically these values corresponded to the physical reading head, the cylinder or track of data, and the slice or sector within the track. Many modern solid-state and flash devices don't have such features, and CHS itself is limited to a max size of about 7.8 GB. So, most devices now-a-days use the Logical-Block-Addressing (LBA) method to locating partition boundaries. The 'absolute starting sector' in a partition table entry is an example of an LBA. (LBA units are in sectors.) CHS is kept for compatibility reasons.

To convert from CHS to LBA, use the following formula:

(C x TH x TS) + (H x TS) + (S - 1) = LBA

Where,

C  = cylinder number
H  = head number
S  = sector number
TH = total heads (255 or 16)
TS = total sectors (63)

Exploration

Try it out! Find a 512 MB - 4 GB SD card or thumb drive and create an image from it using the dd Linux command. Then use a hex editor to inspect the binary values of the image. (The memory size is only for exploratory convenience.) Try searching for partitions and determining their characteristics. (You can use the gparted Linux utility to format the device and fdisk -l /dev/mmcblkX to quickly print out basic MBR info.)

If your SD card is listed under the Linux devices as /dev/mmcblk0 for example, you can create a binary image with the command:

sudo dd if=/dev/mmcblk0 of=$(pwd)/mmc.img bs=512 status=progress

You can then open the file mmc.img in a hex editor such as GHex for Linux or HxD on Windows.

Happy Hacking!


Wednesday, December 31, 2014

DS0 Quad (DS0203) Oscilloscope - First Time Use

As a gift I received a new portable oscilloscope: the DS0203. This, I know will come in handy in the future! Nevertheless, there are a few things to note with its use... First and foremost, this may or may not be able to measure signals faster than 8MHz - I haven't tested anything high than 100KHz yet. Second and more importantly, there is a bit of a learning curve to it's use, something I wish to clear up here.


When I turned the device on, the first question I asked was: "How do I measure a signal?"

For this tutorial, you will need:

  • DS0203 Oscilloscope
  • Probe
  • Signal Source (I used an Arduino UNO and generated a PWM wave on pin 9)



  1. Plug one of the probes into CH-A
    1. Attach the alligator clip to GND (ground)
    2. Mount the probe to the signal source (pin 9)
    3. (optional) See that the probe is set to "X10"
  2. Turn on the DS0203 Oscilloscope
    1. The power switch should be on the lower right
  3. Press the "Play/Pause" button until the screen reads "RUN" in the upper left-hand corner (RUN will continuously capture a signal from the source, while HOLD will allow you to analyze the recorded signal before it changes)
  4. Adjust the settings to improve the signal quality on-screen (Use the leftmost -...+ control to set the value and the rightmost -...+ control to navigate. Use the △ button to switch between horizontal and vertical menus.)
    1. Under channel A (blue), set the values to 0.2V DC. This will vertically scale the signal on screen
      1. Move the leftmost -...+  left or right to change 1V to 0.2V
      2. Press the leftmost -...+ button to switch to signal type and change it to DC
    2. Where it says "AUTO" (orange), set the time measure to about 500uS. This will horizontally scale the signal on screen
      1. Move the leftmost -...+ left or right to change the time to 500uS
    3. Set the threshold "THR" to stabilize the signal and reduce flickering
      1. Press the △ button to navigate the menu on the right
      2. Navigate to THR and press the leftmost -...+ button to select channel A (blue)
      3. Then move the leftmost -...+ left or right to adjust the threshold (left moves down, right moves up; the threshold marker will be label 'T' on the left of the graph)
  5. By now, a fairly decent wave should appear on screen. (These are the exact steps I used when familiarizing my self with the DS0203's environment)


Monday, December 1, 2014

Multi-Rotor Propeller Thrust Formulas

During the construction of my unmanned aerial vehicle (AUV), I needed to know how much thrust my motors would produce, or more, how much mass they could support. The objective is to optimize the mass to thrust ratio using differentiation. After about two hours of research and assistance, I found the formula for thrust, the relation to how much mass the given thrust can support. Next, a friend of mine demonstrated how to solve for air velocity before knowing the math by using the second method of solving a system of equations.

Thrust:

According to AirWolf II, "A commonly used rule is that velocity of the air at the propeller is v=½Δv of the total change in air velocity."


Where,
TThrust which is a kind of force (F).
DThe diameter of the propeller (m)
ρAir density - approximately 1.225 kg / m3
vThe velocity of the air pulled into the propeller (m/s)
Δv   The velocity of the air pushed away from the propeller (m/s)


Next, we can relate mass to the thrust by substituting T with the function for force (Force = mass (m) x acceleration (a)) and solve for m, giving us:


In order to find mass, we need to first know Δv - this is what slowed me down for a little while. So, in order to solve for Δv without first knowing the mass, we'll need to create a second equation (power = force x velocity):


Where,
ηEfficiency of motor (approximately .75)
WThe max wattage of the motor (W = IV or W = I2r  or  W = V2/r)
CThe scaling factor from 0 to 1 (one being maximum throttle)
mMass (g)
aAcceleration
Δv   The velocity of the air pushed away from the propeller

Substitute and solve for Δv:


Then to solve for mass given thrust, simply plug Δv into one of the above formulas and vuala, mass is related to thrust and can be adjusted by either the power of the motors or the size of the propellers:


A real-world example using this formula:
I have brushless motors that run with a max current of 10.5A at 11.1V with a prop radius of 4.5" (0.1143m). Given the following:

C: 100%η: 80%
ρ: 1.225 kg/m3D: 0.2286 m
a: 9.8 m/s2W: 116.55

...each motor with it's mounted prop running at 80% efficiency and the throttle maxed, can carry a maximum approximate weight of 615 grams (or 0.614646... kg). Since I have 6 mounted to my chassis, my AUV will be able to support a maximum [hover] load of approximately 3.69kg. (since the motors are arranged in pairs, this number may be further increased via an increase in efficiency by re-evaluating for v given the previous solved equation)


Friday, May 16, 2014

I2C with the Beaglebone Black

Recently, a colleague asked if I could help with a project that required an I2C bus. This technology is nothing new to me, but his request came with a twist: it needed to be done on a Beaglebone. Well, I couldn't deny this opportunity because I too had a similar project that requested very much the same setup.

I quick bit of surfing surprisingly turned up dry results - most of which were examples in Python. (Why not Ruby?!) Anyway, after several hours of research I finally managed to uncover some valuable hits...

First things first! In order to use I2C one must start up it's engine(s). This is done by entering the following shell command: (There may be two more capes for the other buses, however this is the only one I could find in /lib/firmware; For distros such as Angstrom, I2C should already be loaded)
echo BB-I2C1 > /sys/devices/bone_capemgr.*/slots

REGISTERS
To access i2c registers, the interface clock [ICLK] first needs to be un-gated. This can be done through the Power, Reset, and Clock Management [PRCM] module registers: CM_PER_L4LS_CLKSTCTRL & CM_PER_I2C_CLKCTRL. For this demonstration, the code from the git repository BoneMemory is used - load the shell script with "source script.sh" command.

# Enable I2C2 ICLK and FCLK - otherwise 'Bus Error'
writem 0x44E00000  0 0 # CM_PER_L4LS_CLKSTCTRL.MODULCTRL = 0
writem 0x44E00000 50 2 # CM_PER_I2C2_CLKCTRL = 2

# Prepare bus data and begin

writem 0x4819C000 98 1 # I2C_CNT  = 1
writem 0x4819C000 9C F # I2C_DATA = 0x0F
writem 0x4819C000 A4 8C03 # EN/MASTER/TX/START/STOP

See Pg 3715 of the Technical Reference Manual for a detailed approach to reading and writing via registers.


BASH
(I2C devices are assigned according to the order they were initialized so $Bus=1 may be the I2C-2 bus. Confirm you are using the correct bus by listing them:
                                 "ls -l /sys/bus/i2c/devices"
I2C0 = 0x44E0B000, I2C1 = 0x4802A000, I2C2 = 0x4819C000)

Next is the handy dandy tool that will automagically bit-bang for you to find all connected devices on the given bus: (UU is a reserved address)
i2cdetect -y -r $Bus

To view all the bit values stored in a slave's register file (EE/PROM), one can call:
i2cdump -y $Bus $Addr

To get a particular byte value from the slave's register file, use:
i2cget -y $Bus $Addr $Offset
# Example: i2cget -y 1 0x77 0xB2 w - read a word from 0x77B2

And to write to the register (if permitted by the slave), use:
i2cset -y $Bus $Addr $Offset $Value
# Example: i2cset -y 0 0x52 0x02 0xFF - write 0xFF to 0x5202

These are all script values, but at the same time my priority was set on doing all this in C/C++ and I really didn't want to use the 'system()' function. I knew from my experience with other elements including PWM and GPIO, that there are files one can access in a lower level language such as C that would allow me this I2C functionality, and, after a 45 min presentation (v=8C2zk6B-eLU), I was correct!

The files are located in /dev: i2c-0, i2c-1, & i2c-2

- - - - - - - - - - - - - - - - -
I've tested both the registry and bash approaches with great success!

Wednesday, April 16, 2014

BeagleBone Device Tree Overlays

(All commands and tests were performed in Debian Wheezy, though in most cases, should also work in various other Linux Distro's)

Real quickly, what is a device tree [DT] overlay? Well, just like a .config file - such as those used in Minecraft Mods - a device tree overlay describes how the kernel is wired in order to take advantage of any new hardware peripherals dynamically - in the case of the bone, it is most commonly used to describe which pins are wire to what device and who has compatibility.

Let's review over the structure of a simple device tree: (Thanks to Adafruit for their valuable detailing)
Unfortunately, Debian and several other distros do not come with device tree source files, just the compiled blobs. This was unfortunate for me as I had four Debian-imaged bones and one Angstrom bone that I forgot the login info to... after several hours I managed to reimage the Angstrom copy to see if the source files were even there, and thankfully they were!



EDIT: While browsing through the raw Linux Kernel source code, I found a number of other *.dts files linking to many other devices. To find specific names and values for these trees, the kernel tree its self has all the answers - it may be intimidating at first but really I feel it is no different from Minecraft: Kernel=Vanilla, Drivers=Mods, Hardware Descriptors=Config Files. The Beaglebone Black uses an OMAP3 [OMAP3630] microprocessor also references as AM33XX in the source code. Linux Source Code



This is the version of the DT, which is 'version 1'
/dts-v1/;

This comes next, tagging the DT as a plugin
/plugin/;

Without specifying compatibility, this plugin would not work
compatible = "ti,beaglebone", "ti,beaglebone-black";

Next is the identification, including the DT name and version - which seems to always be '00A0' (this is also the name of the file in the form NAME-VERSION.dtbo)
part-number = "BB-BONE-DT-NAME";
version = "00A0";

Include any and all resources to be used. (In some cases, just the pins are enough, otherwise it is always good to include the device the pins will be muxed to as well) Here the pin is specified in the form HEADER.PIN, the first pru will also be used:
exclusive-use = "P9.27", "pru0";

And finally the fragments, which can be seen as partitions, or 'structs' in a c-stylized way of looking at things. A device tree can have as many fragments as it needs, but for the simplest layouts, one only needs two: the first for pin muxing and the second for device initialization and pin binding. The syntax goes as follows: fragment@NUM { }; so fragment@0 {}; or fragment@2 {};

A fragment will begin with a target, followed by the __overlay__ block, and within that block resides the settings to be applied to or through the targeted device. For example:
fragment@0 {
    target = <&am33xx_pinmux>;
    __overlay__ {
        token_name: sub_name {
            pinctrl-single,pins = < 
                0x1a4 0x25 
            >;
        };
    };
};
The target is am33xx_pinmux and through the target via 'pinctrl-single,pins,' pin P9.27(0x1a4) is muxed to pin mode 5 as a 'fast-input' with an internal pull-down (0x25) - this prepares the pins for PRU0 output. The 'token_name:' is referenced in other fragments further down.

When initializing a device in the device tree, one must set the status to 'okay.' Then in order to do anything with that device, one must assign the pins created earlier:
fragment@2 {
    target = <&pruss>;
    __overlay__ {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&token_name>;
    };
};
The pin names can be left as 'default.' Sometimes there are other names that can be added for state manipulation such as found in the source file: cape-boneblack-hdmi-00A0.dts: pinctrl-names = "default", "off"; which can be seen in action through shell:
# cat /sys/devices/ocp.*/hdmi.*/pinmux_state
default
# echo off > /sys/devices/ocp.*/hdmi.*/pinmux_state
# cat /sys/devices/ocp.*/hdmi.*/pinmux_state
off
# echo "test" > /sys/devices/ocp.*/hdmi.*/pinmux_state
-sh: echo: write error: No such device



The following is a list of devices that can be modified or used via device tree overlays:
am33xx_pinmux    ocp - on chip peripherals
tscadc - analog digital convertermac - media access control
eCAP0_in_PWM0_outeCAP1_in_PWM1_out
eCAP2_in_PWM2_outtps - transaction processing system
mcasp0 - multichannel audio serial portmcasp1
eMMC_RSTngpmc - general purpose memory controller
pruss - programmable real-time unit subsystem pru
pru0pru1
spi0 - serial peripheral interfacespi1
uart1 - universal asynchronous receive transmituart2
uart3uart4
uart5uart6
i2c0 - inter-integrated circuiti2c1
i2c2intc - interrupt controller
ehrpwm0 - enhanced high resolution pwmehrpwm0A
ehrpwm0Behrpwm1
ehrpwm1Aehrpwm1B
ehrpwm2ehrpwm2A
ehrpwm2Bepwmss0
epwmss1 - enhanced pulse-width mod. subsystemepwmss2
ecap0 - enhanced captureecap1
ecap2edma - enhanced direct memory access
lcd - liquid crystal displaylcdc - liquid crystal display controller
aes - advanced encryption standardsham
usb_otg_hstps-bl
dcan0dcan1
clkout2mmc1 - multimedia card
mmc2mmc3
timer1timer2
timer3timer4
timer5timer6
timer7wdt
gpio1 - general purpose input outputgpio2
gpio3gpio4
gpio0_0-31gpio1_0-31
gpio2_031gpio3_0-31
P8.1-P8.46P9.1-P9.46
rtcusb - universal serial bus
sgxmdio
..



To compile the source, run the following shell command:
dtc -O dtb -o $NAME-$VERSION.dtbo -b 0 -@ $NAME-$VERSION.dts

To apply the new overlay, copy the *.dtbo file to /lib/framework and run the command:
echo $NAME > /sys/devices/bone_capemgr.*/slots

Confirm that it has loaded:
cat /sys/devices/bone_capemgr.*/slots

And to remove it, use the slot number where it is found:
echo -$SLOT_NUM > /sys/devices/bone_capemgr.*/slots





Tuesday, April 15, 2014

Pestering Adware

Though I may be a Linux user, and although there are programs such as 'wine' for emulating Windows-based apps in Linux, there is still software out there that can only run in Windows - including various Autodesk, Adobe, and Corel products,  and so I remain in a windows environment. And as every seasoned Windows user knows, no matter how good one's anti-malware,  -spyware, -adware, or anti-virus software is, there is always the rare occasion where it cannot do the job alone.

www.superfish.com, api.jollywallet.com, istatic.datafastguru.info, these are some of the URLs that pestered me for months and none of the programs I had available could find them. Thanks to Chrome's Console (Right Click > Inspect Element > Console) and the step-by-step descriptions provided here, I identified the foreign object and successfully removed it from my machine - no uninstalls and no system wipes!




(The following is for advanced users - a more descriptive approach can be found in the link provided above)
STEP 1:

  • - In the Registry Editor, navigate to:
    HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Policies\Google\Chrome
  • - Delete 'ExtensionInstallForcelist' or delete all the data within.


STEP 2:

  • - Go to chrome://extensions/ and check 'Developer mode'
  • - Search for all extensions that contain the line "Installed by enterprise policy." Save their name and id
    (In my case, there was only one extension with the following information, NAME: YTBBloCkeaRApp ID meajhoiolabnglbkdopdglmmphjckgfp)
  • - Go to chrome://policy/ and click 'Show value'
  • - Next to the ID should be a path to the malicious content. Navigate to that folder destination and remove the respective content.


STEP 3:

  • Go to %AppData%\Local\Google\Chrome\User Data\Default\Extensions
  • Delete the folder with the given ID


STEP 4:

  • Go to C:\Windows\System32\GroupPolicy
  • Delete 'Machine' and 'User' folders (following this step, Chrome will notify that the unwanted extension has been uninstalled)


BONUS:

  • Re-scan to ensure all suspecting evidence is removed.




Back Story:
    When browsing the web, I ensure that I have the following Chrome extensions installed and running: HTTPSEverywhere, AdBlocker, ScriptSafe (or no-script), and Blacklist. ScriptSafe has been a wonderful extension that allows me to be selective in what runs under the hood and what doesn't - it's like disabling JavaScript but only for some websites - saving me the worry of malicious scripts installing bad subjects on my system. One of the only downsides to this is that most sites use third-party scripts to help their website function the way they intended it. Some of the most common third-party scripts is jquery.min.js, three.js, and node.js which are like JavaScript libraries that save a developer a lot of time programming and designing.
    About month or two ago, when attempting to reestablish functionality to a certain site, I mistakenly enabled an unfamiliar script. It fit right in with the others... I later found my self with about 3 trojans and 12 other objects. Malwarebytes, SpyBot, and CCleaner did what they could, but my browser's settings where altered - a new homepage, a new tabpage. Most of this was resolved with a quick reset, nevertheless, I noticed ScriptSafe displayed traces of www.superfish.com, api.jollywallet.com, and istatic.datafastguru.info. A colleague pointed me to the Chrome Extensions; sure enough there was an unauthorized extension. Removing it solved the problem - for a time.

Friday, April 4, 2014

Explorer DVL Wiring

It is unfortunate how much time has past trying to make this device work, and kind of sad to realize how easy it was in the end, but after a few phone calls and several strait hours of effort, our team finally managed to pull data from our Explorer Doppler Velocity Log from RD Instruments:


The problem was the wiring from sensor to computer. The documentation also was a little vague - indeed, a pinout table can be found on page 12 of the manual, but there are no further details about what the pins actually do like an integrated circuits' manuals would. Here are the pins and their values:
  • 1: RX+  Messages received by the DVL Unit it's self.
  • 2: RX-  (Differential pair - RS422)
  • 3: TX+  Messages transmitted by the DVL Unit it's self.
  • 4: TX-  (Differential pair - RS422)
  • 5: COMM 1_2  0 = Com 1 (DVL Unit); 1 = Com 2;
If additional sensors and other devices were to be integrated with this as a full system, these pins would be used. (Our team never had a need as all other sensors were driven with real-time units)
  •  6: RX2+;   7: RX2- (Diff. Pair);
  •  8: TX2+;   9: TX2- (Diff. Pair);
  • 11: RX3+;  12: RX3- (Diff. Pair);
  • 13: TX3+;  14: TX3- (Diff. Pair);
  • 15: COMM 3_4:  0 = Com 3; 1 = Com 4;
  • 16: RX4+;  17: RX4- (Diff. Pair);
  • 18: TX4+;  19: TX4- (Diff. Pair);
In some cases, a vehicle may contain more than one DVL and one cannot ping at the same time another does, otherwise things would end up in complete chaos. So, if one wishes to use say 4 at the same time, they would need a way to synchronize the pings. This can be done with the following pins: (NOTE: the following values are out of speculation, nevertheless their function has been confirmed by the engineers at RDI)
  • 21: TRIG_IN   0 = wait; 1 = ping;
  • 23: TRIG_OUT  0 = busy; 1 = finished;
And the various other pins that are good to have:
  • 33: CHAS_GRND  This would attach to the vehicle chassis
  • 34/35: VDC+  12-24V 3A MAX
  • 36/37: VDC-  0V 0A
Now for the solution to our original problem - how to wire the device to a computer - for that I drew up a valuable schematic:

DB37-1 is connected to DB9-3: RX goes to TX
DB37-3 is connected to DB9-2: TX goes to RX
DB37-5 is connected to DB9-5: COMM 1_2 goes to GND

(I want to note that I happened to mix up pins 1 and 3 causing me grief for several hours. I didn't catch this silly mistake until I used an Arduino Uno to intercept the serial messages. The Arduino was reading data on the TX line but what's funny is that the data it was reading, originally came from the computer and not the DVL, Oops. In the end I ended up with TX to TX and RX to RX - that surely is not going to work, switch 'em around!)

Next, for up-to-date systems, a simple RS232 to USB adapter can be used. RDI recommended us the EasySYNC ES-U-1001, though I've tried another brand and the result was still the same.

With BBTalk (or the Arduino COM port), one can send and receive messages to the DVL.
Project: Success!