tag:blogger.com,1999:blog-9668868998412204422024-03-04T22:37:28.046-07:00St Kwan's Home for the Terminally ADDNihil est tam malum ut non possit pejuskwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.comBlogger224125tag:blogger.com,1999:blog-966886899841220442.post-88908395529484231722023-09-23T12:31:00.003-06:002023-09-23T12:32:45.435-06:00Gravity Simulator #1,000,006<p>I've written a lot of gravity simulators. This one is the first time I've built one into a game engine. First, I created a Node2D and a Sprite2D with a texture so it's visible. I then attached the following code to the sprite node:</p><p><br /></p>
<pre>extends Sprite2D
var v:Vector2=Vector2(100,0)
var center:Vector2=Vector2(500,200)
var mu=3e6
# Called when the node enters the scene tree for the first time.
func _ready():
var vec=Vector2(500,0)
global_position=vec
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
var r:Vector2=(global_position-center)
var a:Vector2=-mu*r/r.length()**3
v+=a*delta
global_position+=v*delta
</pre>
The _process() function is a very simple Euler integrator. The acceleration is the standard test-particle gravity equation of motion, where the center is considered to be infinitely heavier than the test particle, such that the gravity of the test particle doesn't affect the motion of the center. <div><br /></div><div>The next obvious things to do:
</div><div><ul style="text-align: left;"><li>Use Runge-Kutta instead of Euler</li><li>Gravitate towards another sprite instead of an invisible point</li><li>Give both points finite mass </li><li>Gravitate towards any number of masses, to make an N-body simulator.</li></ul><div><p>It looks like I might be right about how to do physics in Godot, but it still doesn't feel quite right. Godot includes ragdoll physics and joints. In order to integrate custom physics, I would have to be able to generate custom forces and moments, instead of directly doing the numerical integration myself. I expect something more like:</p><p>
<pre>
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
var r:Vector2=(global_position-center)
var F:Vector2=-mu*global_mass*r/r.length()**3
add_force(F)
var M=... #calculate moment on the object
add_moment(M)
</pre>
This way if the engine does a higher-order integrator or has its own forces, this slides in nicely.
kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-29711523437051832002023-09-23T10:19:00.005-06:002023-09-23T12:04:52.492-06:00func _process(delta)<p> I wish that GodotScript was just Python. It isn't, for a reason, but I don't know the reason yet. It could be:</p><p></p><ol style="text-align: left;"><li>GodotScript came first, or before Python was widely known.</li><li>Python is too hard to integrate. In this case, they considered *creating an entire new language interpreter* easier than incorporating Python</li><li>True Python implies that the entire marketplace of Python libraries can be used, and that may have been too difficult to achieve.</li><li>Python doesn't match the authors' internal model for how to do scripting</li><li>Godot was started as a personal project, and a scripting engine was one of the "fun" things that they wanted to do with it.</li></ol><div>In any case, I have reached the part of the tutorial where scripting is introduced. The template for a script for a node includes the following interesting code:</div><div><br /></div><p></p><div><span style="font-family: courier;">extends Node2D</span></div><div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"># Called when the node enters the scene tree for the first time.</span></div><div><span style="font-family: courier;">func _ready():</span></div><div><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>pass # Replace with function body.</span></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"># Called every frame. 'delta' is the elapsed time since the previous frame.</span></div><div><span style="font-family: courier;">func _process(delta):</span></div><div><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>pass</span></span></div></div><div><br /></div><div><br /></div><div><span style="font-family: inherit;">The interesting one is _process(). I can imagine a mode of using the game engine where I just use it as a graphics engine -- every frame, this function is executed by the graphics engine, where it does all of my custom physics etc, changes the position properties of a bunch of nodes, then returns and lets the graphics part of the engine do its thing. </span></div><div><span style="font-family: inherit;"><br /></span></div><div><span style="font-family: inherit;">Seriously, this function can implement a Runge-Kutta numerical integrator from the ground up -- it could use the node properties as part of its state, can keep global or static variables for the rest, and implement any law of motion I can think of. I'm not smarter than Newton or Euler and I intend to use their laws, but I don't *have* to.</span></div><div><br /></div><div><span style="font-family: inherit;">If this was all Godot did for me, it might be just right. I'm still exploring, seeing what else it can do for me (I'm on part 25 of 47 of the <a href="https://academy.zenva.com/course/intro-to-godot-4-game-development/" target="_blank">Intro to Godot course</a>). I strongly suspect that the engine does have a physics component though, since I have seen "gravity" as a property of some of the nodes.</span></div><div><span style="font-family: inherit;"><br /></span></div><div><br /></div>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-70539251722148227942023-09-22T10:44:00.003-06:002023-09-23T12:04:21.429-06:00Fallout from the Unity Disaster<p> I have always been a low-level kind of programmer. I have a good intuitive sense of How Things Work, but I tend to build my own mental abstractions on top of these and I often have trouble learning other peoples' abstractions.</p><p>For instance, one of my unfinished projects is making math explainer videos. I knew about Manim from 3blue1brown, but he uses a completely different mental model of how animations work. It's tough to argue with success -- he has hundreds of videos and millions of subscribers, while I have zero and zero. Plus, I could never get the dancing equations to work as well in PictureBox as he does with TransformMatchingTex().</p><p>Similarly, I have always wanted to get into 3D simulator development -- not necessarily a game, more like a virtual environment where I can program virtual robots. One of my other long-term unfinished goals is to animate the launch of various historical spacecraft, much like Jim Blinn animated the flybys of Voyager. I want to be able to write a physically realistic rocket guidance program similar to what a Titan would have actually used, and use that to create a physically accurate, properly scaled animation of the launch. The legend goes that the second Voyager launch (Voyager 1, don't ask) only had about two seconds of fuel left in the upper stage when it finished the boost. How much did the first launch (Voyager 2) have? Is two seconds a lot? This can't really be answered without proper scale.</p><p>Anyway, one of the things I have been avoiding as someone else's abstraction, is game engine design and usage. I always thought myself capable of writing my own, even though I kept bouncing off of OpenGL and *its* abstractions. I have written simulator engines any number of times, basically just fancy numerical integrators. I have never learned anyone's game engine -- I have never explored anyone else's abstractions.</p><p>In the last couple of weeks, the Unity game engine has claimed authority to alter its relation with its developers. </p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/WpE_xMRiCLE" width="320" youtube-src-id="WpE_xMRiCLE"></iframe></div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><p>Lets just say that the development community hasn't taken it well.</p><p>One aspect of the fallout of this announcement is a mad scramble to other engines. It might be too late for programs which are years along and too intertwined with Unity, but many many people are looking at alternatives, and one of the ones that came up is Godot (<a href="https://godotengine.org/">https://godotengine.org/</a>). This one is free and open source, so it can't help but stay free.</p><p>Even though it is free, I did spend about $26 on an online course for it (<a href="https://www.humblebundle.com/software/everything-you-need-to-know-about-godot-4-encore-software">https://www.humblebundle.com/software/everything-you-need-to-know-about-godot-4-encore-software</a>). The biggest thing I am looking for is how well their abstractions match up with my own. This will be interesting, as I'm not even sure what I expect a game engine to do for me. How does the physics part work? Can I write my own physics? Is there a numerical integrator underlying things? Are translational and rotational kinematics already implemented, and I just have to write my own dynamics? The engine might do too little, and require me to implement my own numerical integrator, or it might do to much, and implement things in such a way that I can't do a rocket.</p><p><br /></p>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-32707311442985267512023-05-31T16:24:00.001-06:002023-05-31T16:24:36.226-06:00Velocicoaster<p> On Friday, May 26, I rode the Velocicoaster in Universal Florida. I almost didn't for a couple of reasons, but I am glad I did. I'm not quite as sure I would go on it again, but I did offer to do so with my niece once she heard that I went on it.</p><span><a name='more'></a></span><h2 style="text-align: left;">Tester seat</h2><div>I am right on the upper edge of weight for being able to ride roller coasters like this. Universal has tester seats in front of most of its coasters. I fit fine into Hagrid, but did not fit into Hulk.</div><div><br /></div><div>On Velocicoaster, the seat is the same shape as the real ride (of course) and has the same restraint as the real ride (of course) except that instead of locking, it has a red or green light. The first time I tried, I didn't get the green light. I was close, but not quite there. I walked away, kinda dissapointed, mostly in myself because I was used to fitting into things. I walked away, then realized I was wearing my fanny pack and that it might make the difference.</div><div><br /></div><div>When I went back, there was a <strike>cast</strike> team member there. I took off my fanny pack, filled it up with the contents of my pockets, then sat back in the tester seat. The team member was able to push the restraint down enough to get the green light to come on, and he said that if I could handle about 5 minutes of that, I would be fine.</div><div><br /></div><div>Armed with that knowledge, I went on the ride.</div><div><br /></div><h2 style="text-align: left;">Single Rider</h2><div>The ride has three queues -- normal, single rider, and express. I chose the single rider because I <i>was</i> single -- my nieces were elsewhere exploring Diagon Alley. The queue seemed to be similar length, but diverted after the first room where the friendly DNA molecule was explaining the lockers and metal detectors.</div><div><br /></div><div>The Velocicoaster is <i>very</i> strict about nothing in pockets -- we shall see why in a bit. Guests are required to empty their pockets, put all bags etc into lockers, then pass through a metal detector. The only thing we are supposed to keep is our tickets. Since my ticket was on my phone, I was given a locker ticket. I was a bit confused, because I didn't see the barcode on the locker ticket at first. I tried to scan my phone ticket bar code, but that didn't work and the team member had to show me how to use the ticket. It was quick and he was trying to get people through as quickly as possible, but that was the closest any team member came to being rude.</div><div><br /></div><div>Once my stuff was in the locker, I went through the metal detector where of course my belt set it off. I chose to keep my belt as I needed it to keep my pants up, but this belt is about the worst for metal detection -- it has both a metal buckle and metal eyes in all the holes all the way along. After being wanded and confirming that it was just the belt, I went on.</div><div><br /></div><h2 style="text-align: left;">Story</h2><div>I only ever read the first book and saw the first movie -- that story had a satisfying conclusion to the point where I didn't want the story to continue. So, I don't know the characters, as apparently they weren't even in the original story, only being introduced in <a href="https://en.wikipedia.org/wiki/Jurassic_World" rel="nofollow" target="_blank">Jurassic World</a>. The characters in the story are Claire who works in the corporate suite, and Owen who works directly with the dinosaurs. Claire is there to give the people what they want, and apparently the people want "more teeth". Owen, on the other hand, doesn't think it's a good idea to run a rollercoaster through a dinosaur paddock.</div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/TDCQoPd55y4" width="320" youtube-src-id="TDCQoPd55y4"></iframe></div>Since I'm not there for the theme, just the coaster, and I'm not that involved in the further Jurassic Park world, the theming doesn't really matter for me and I would have been just as happy with a bare coaster.<div><br /></div><div><h2 style="text-align: left;">Loading</h2><div>Since I was a single rider, people from my queue were just used to fill gaps. Whenever there is a party with an odd number of people in it, they are assigned enough rows, then a single rider is taken to fill the gap. The trains are two abreast, with at least 10 rows (I think I was in 9). Note that they didn't try to combine two odd-numbered parties -- if there are two consecutive parties of 3, that uses two single riders, not zero.</div><div><br /></div><div>The team member was able to get the restraint closed enough that I could ride. It felt quite tight, but not uncomfortably so. I don't remember it this way, but everything I have seen says that the restraint isn't on the shoulders, but on the lap and thighs. The on-ride photo backs this up.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2xSLSCQ5TbB4xPR845-rhW_4tN-uQVQRQoOkFz-NyxA3WZZHltJqx2X2WpChmKehY2Gg8VQwklPq50jUVko6sWphg8ExT2yAYH81in1EAB_biuoVIgL9q9IYSKNd6ux1GuVkP1hiYWrLRgzND4aaaE8Tjrn81kVVxf9GRRSRDQWccN1m2GNPty3j0/s732/20230526_165845.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="569" data-original-width="732" height="249" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2xSLSCQ5TbB4xPR845-rhW_4tN-uQVQRQoOkFz-NyxA3WZZHltJqx2X2WpChmKehY2Gg8VQwklPq50jUVko6sWphg8ExT2yAYH81in1EAB_biuoVIgL9q9IYSKNd6ux1GuVkP1hiYWrLRgzND4aaaE8Tjrn81kVVxf9GRRSRDQWccN1m2GNPty3j0/s320/20230526_165845.jpg" width="320" /></a></div><br /><div><br /></div><div><br /></div><h2 style="text-align: left;">The ride</h2><div>The one thing I don't like about roller coasters is negative-g, the feeling of being pulled out of the seat. I think it's mostly because I don't instinctively trust the restraint to not pop open when being pulled out. The Velocicoaster definitely has negative g, of at least two types. Going over some of the hills, the car is pulled down by the track faster than gravity. In another spot, the track is perfectly straight and level, but upside down, giving a -1.0g the whole time.</div><div><br /></div><div>Obviously the restraints are safe, but it didn't feel like it while I was riding. I definitely felt pulled out of my seat, as if I wasn't in tight enough.</div><h2 style="text-align: left;">Final evaluation</h2><div>After I got off, I sent a picture of the ride photo to the nieces. They replied that they wanted to go on it. It turned out that we didn't get a chance to do so, but I was ready to ride again if they chose to. If you like roller coasters, can handle negative g, and are a little bit smaller than me, you should ride this ride. In the game Roller Coaster Tycoon, they use the term "excitement" for the quality of a ride that makes it fun and makes people want to ride it and/or ride it again, and "intensity" for the quality that makes people scared/sick after riding it. Excitement has no upper bound -- the more exciting, the better. Intensity on the other hand has a limit, different for each person, such that intensity beyond that point makes the ride less fun. A good roller coaster has high excitement and at least medium intensity, and I would describe Cosmic Rewind as perfectly hitting those. Velocicoaster is definitely more intense that Cosmic Rewind, but I would call it less exciting. I would ride either again, but I would definitely prefer Cosmic Rewind.</div><div><br /></div></div>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-36288973694202214442023-04-17T06:34:00.000-06:002023-04-17T06:34:56.842-06:00Shipometer proge<p> We are on Shipometer 23.04 now. Shipometer 23.03 is a failure, for perhaps multiple reasons.</p><p>First, the shipometer is a HAT for a Raspberry Pi that can carry a ZED-F9R GPS+IMU, a BME280 pressure/temperature/humidity sensor, and an ICM20948 9DoF sensor. Even though the GPS+IMU has an IMU built into it, I want the ICM20948 because I can control it at a lower level, and because it has a magnetometer in it as well. </p><p>Finally, it carries an LPC210x as a precision timer. The ZED-F9R generates a pulse-per-second (PPS) which is accurately timed to have its rising edge right at the top of each second, with sub-microsecond accuracy. This PPS is routed to and captured by the Pi, but is also routed to the timer capture input of the LPC210x. This microprocessor is an old ARM7TDMI, but has a hardware 32-bit timer capable at running at 60MHz, with multiple usable capture inputs. The LPC210x is programmed to count at 60MHz, reset after 3.6 billion cycles, capture several inputs, and output the exact timer count of each pulse to its serial port, which is wired to the Pi UART1. This way, the time of each PPS and sensor data-ready signal can be recorded with no latency due to things like interrupts and real-time software.</p><p><span></span></p><a name='more'></a>Anyway, I originally designed a HAT which had all these parts directly soldered. And that scared me to death. The ZED-F9R costs over $200, and I couldn't stand to think of it soldered to about 174 other parts, all of which would have to have been soldered perfectly. Besides, this was back during the Chip Shortage of 2022, which made it impossible to get my ICM20948 sensor as its own part. I happened to have one on a breakout board, so I designed a HAT that had the ZED-F9R soldered directly to it, along with the LPC210x and some glue logic. It also had a socket for the ICM20948 breakout board, as well as the BME680 pressure/temperature/humidity/gas sensor that I had as well.<p></p><p>First, I laid it all out on a breadboard. I got everything working together, if not all at once. I had the GPS running and syncing the Pi. I had an LPC2102 on its own breakout reading the PPS of the GPS as well. I had the sensors attached to the chip select glue logic and working as well. I then designed the HAT based on that.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQdeMgk-Fhv2tiE7hOjY2Shxrh8xABd6bPRvtn_BAlYFOWOBNPFoiMCZyIio4jw2juZDR9SOutzbf473sYWhu2_fClvh7nmca78oLz756wn3XaCiMYff7dobSlzJex4hEZWfXSdgcxph8Ti77uN_3DT8quLKaqGlWObqNlsBJRhDsKSkwcS3_Orwc8/s4032/20230319_002152.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="1816" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQdeMgk-Fhv2tiE7hOjY2Shxrh8xABd6bPRvtn_BAlYFOWOBNPFoiMCZyIio4jw2juZDR9SOutzbf473sYWhu2_fClvh7nmca78oLz756wn3XaCiMYff7dobSlzJex4hEZWfXSdgcxph8Ti77uN_3DT8quLKaqGlWObqNlsBJRhDsKSkwcS3_Orwc8/s320/20230319_002152.jpg" width="144" /></a><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihjb7BiCuluH64cHpNyMoPr4viwEB9-svsAYIBCysjHFd_GBb9znoeBu9v8CfWlbXz4JJ9APGIh1Iml6UHcYJHs5ZnAWwo0UXEO3Hlldow5ImJmYFxccckINzFUJO_Tpc2mTgPk_7rJgUFOI6F3XAjS4MhHnpb1Y8yBTeXw6uwj1lnIAZ4A55tcXnA/s4032/20230121_223218.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="4032" data-original-width="1816" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihjb7BiCuluH64cHpNyMoPr4viwEB9-svsAYIBCysjHFd_GBb9znoeBu9v8CfWlbXz4JJ9APGIh1Iml6UHcYJHs5ZnAWwo0UXEO3Hlldow5ImJmYFxccckINzFUJO_Tpc2mTgPk_7rJgUFOI6F3XAjS4MhHnpb1Y8yBTeXw6uwj1lnIAZ4A55tcXnA/s320/20230121_223218.jpg" width="144" /></a></div></div><br /><p>The first HAT I designed for this project is the Shipometer 23.03 (since it was ordered in March 2023). I ordered all of the parts from Digikey and Mouser (I actually got the GPS months ago, waiting for something like this), a board from OSH Park, and a stencil from OSH Stencils. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT1vUzSp680b1oyLs6RRXSf_786NhVaUAdASxnaQQkLpNj5ZqWevFgcj1ICoUkersOVcdSNGXMuS0mgTdG674_aZOX9XGcVuNPli6m2pE1Q3926kXd4WIisNqqxYJSxBWeM_9eLKOCB9A1uOe7ha-U1wpfE2HZVrrAfUzZUycd3TWFZin9QMAhAPGi/s1824/20230415_201613.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1578" data-original-width="1824" height="277" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT1vUzSp680b1oyLs6RRXSf_786NhVaUAdASxnaQQkLpNj5ZqWevFgcj1ICoUkersOVcdSNGXMuS0mgTdG674_aZOX9XGcVuNPli6m2pE1Q3926kXd4WIisNqqxYJSxBWeM_9eLKOCB9A1uOe7ha-U1wpfE2HZVrrAfUzZUycd3TWFZin9QMAhAPGi/s320/20230415_201613.jpg" width="320" /></a></div><br />I carefully placed all the parts on the board, then baked it in a toaster oven. This is the first problem -- I wanted to be sure that the pads under the GPS were done, so I left it a little longer than normal. Usually I pull it 10 seconds after I see the paste melt, but this time I left it longer, until the sticker on top of the GPS started bubbling.<p></p><p>I ended up with a great-looking part that didn't seem to work. The LPC210x programmed once, and the GPS never came alive at all -- either by USB or as attached to the hat.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnB9Rh7sprFVz9HqWjd9cAzipewSMnTpS9-TGklLgBfMYbEgIGcd5tIAAGNhNvs4IH_9oolev18UDWBXPSY2auKO8bUN77WV7pK8wfXfpo5caFbReDozOLdx4XRJ7MRjwjKem0NNX-JLaYbdAvyTldMUm-SzbysCli3sXFzW0i7QFj2GRcU58Xtc23/s4032/20230415_201036.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1816" data-original-width="4032" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnB9Rh7sprFVz9HqWjd9cAzipewSMnTpS9-TGklLgBfMYbEgIGcd5tIAAGNhNvs4IH_9oolev18UDWBXPSY2auKO8bUN77WV7pK8wfXfpo5caFbReDozOLdx4XRJ7MRjwjKem0NNX-JLaYbdAvyTldMUm-SzbysCli3sXFzW0i7QFj2GRcU58Xtc23/w400-h180/20230415_201036.jpg" width="400" /></a></div><p>That was disappointing. </p><p>So, I resolved to not solder as much the next time. Shipometer 23.04 is more of a socket -- it just has the LPC210x and chip select logic. This is small enough that I am comfortable soldering it -- all of the parts are cheap enough that I could have made this board multiple times. In order to support the sensors, I came up with something that I am inordinately proud of -- the stamp. Both of these sensors can be either I2C or SPI (I am using SPI) and therefore have similar interfaces. Both devices have an AD0 pin for selecting one of two I2C addresses, and one has an data-ready output (which is what I am timing with the LPC210x). Since they are so similar, I put them both on castellated boards with compatible pinouts. I <i>could</i> interchange the devices, and they would work, but each part actually does have an intended slot. Specifically, the data-ready output would be on the wrong part.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhlEE4AeWCsgZbIpnLTkTcLTgjtOIhexpp5vfbmjjKaiT_OwDDRIQg1sOZdIlroEGGrlMXKnbniEpDTgUD6qXUcW6NIyteUxmV7ObCcdKuase-yB9ildCMLJt9PpTctNhgKh9dKX6ZLIV6DQNu_HvsQ6OGX33W9aLgpePO-aeK8DtV0Mhm_zFXni2n/s4032/20230415_205213.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1816" data-original-width="4032" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhlEE4AeWCsgZbIpnLTkTcLTgjtOIhexpp5vfbmjjKaiT_OwDDRIQg1sOZdIlroEGGrlMXKnbniEpDTgUD6qXUcW6NIyteUxmV7ObCcdKuase-yB9ildCMLJt9PpTctNhgKh9dKX6ZLIV6DQNu_HvsQ6OGX33W9aLgpePO-aeK8DtV0Mhm_zFXni2n/w400-h180/20230415_205213.jpg" width="400" /></a></div>All of the SPI interface is on the left side, while the I2C and data-ready is on the right. In the case of these two sensors, the parts know how to be I2C <i>or</i> SPI depending on their inputs, so a couple of the SPI pins are just wired to their corresponding I2C pin. Finally, the part has 3.3V power supplied at the upper right, 1.8V at the upper left (the stamp just leaves it not connected if 1.8V is not required) and all I/O is at 3.3V. In this case, the ICM20948 is a native 1.8V part, so the stamp carries a level shifter.<div><br /></div><div>Since soldering a stamp commits it, and committing a bad stamp will ruin both the stamp and the board it is attached to, I designed the stamp tester. It is mostly just a set of pins, spaced slightly closer than the stamp width so that each pin applies pressure to the stamp. This allows the stamp to be tested without soldering anything. The stamp tester also includes a 1.8V regulator to supply the upper left corner.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9K5HSJ--VJmKYGzRN2pVnfuWkUnBClJDJ165m8kW4BwEt2i9lugz1EiZDdBT1KnrRr2zDbzxj_w6eivX7B8MfxuiLwy12Chh7Qc9_AIGrOR4_9oYk_zI0xspUWGy9sJd66NdE5uPtOH3XVwFnotC8K8kVVsjjprfUVwDbp9bI-B8tmXCNCto_ecFp/s4032/20230415_205257.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1816" data-original-width="4032" height="144" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9K5HSJ--VJmKYGzRN2pVnfuWkUnBClJDJ165m8kW4BwEt2i9lugz1EiZDdBT1KnrRr2zDbzxj_w6eivX7B8MfxuiLwy12Chh7Qc9_AIGrOR4_9oYk_zI0xspUWGy9sJd66NdE5uPtOH3XVwFnotC8K8kVVsjjprfUVwDbp9bI-B8tmXCNCto_ecFp/s320/20230415_205257.jpg" width="320" /></a></div><p>The main board includes the LPC210x I talked about before, along with a chip-select decoder. The Raspberry Pi only comes with two chip-select lines, while at one point I needed up to four -- one for the BME280, two for the LSM9DS1 inertial sensor that I was also investigating (one for inertial and one for magnetometer), and one for the LPC210x. I figured out a clever way to get up to 8 separate SPI devices out of this -- I use up to 3 GPIO pins on the Pi as a chip address, and run those through a 3-to-8 active-low decoder. I don't think I was the first to discover this, as the 74HVC138 is perfectly designed for this purpose. It has its own enable logic, which sets all of the output pins high (disabled) if this input is disabled. If the chip is enabled, exactly one of its 8 outputs will be set low, enabling the chip select on that one device. We hook the actual chip select of the Pi to the enable of the decoder, and the address GPIO to the address input. We then hook all of the outputs to each device's chip select line. The Pi software will set the address using its GPIO pins, then use the normal SPI driver to talk to what the driver thinks is the one-and-only device out there. When the driver activates the chip select, it actually activates the decoder and activates the one addressed chip select line.</p><p>As it happens, I expect to use the serial UART to communicate from the LPC210x to the Pi, and I am using the ICM20948 and not the LSM9DS1, so the CE0 and CE1 pins would have been adequate. Since my software is already expecting to address multiple parts, I decided to leave the decoder in place.</p><p>Finally, the hat contains both a 3.3V regulator and a 1.8V regulator. The 3.3V draws off the 5V pins of the Pi, and the 1.8V draws off the 3.3V. There is a fan header on the hat, and I didn't like the idea of attaching a motor to the same 3.3V line that was driving a bunch of sensors.</p><p>Finally, there is a socket for the ZED-F9R breakout from Sparkfun (that I already have and have been testing with).</p><p>Assembling the BME280 and ICM-20948 stamps both worked fine. I used the stamp tester to run both of them independently with the Pi. Now for the problem. When I dropped the ZED-F9R into the socket on the hat, it lit up and eventually indicated it had a lock (by blinking its own on-board PPS light). However, it never showed up in the UART. This exact same part worked on the breadboard, so I didn't know what the problem was until I looked up the pin assignemnt. I am using UART3 for talking to the GPS, which is using GPIO4 for TX and GPIO7 for RX. I know that TX on one device gets connected to RX on the other, but somehow I still screwed it up on the schematic:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgSpiJRVJ2oG6x9PnFKWX1Hq0_lctmOSzZrF2kMcVYecuxV9XS-N_blpD6vnN8VQJl9zwRia3psBEa8GEMqg71_jCiCRlFmEXbShlxmgcnY4q1wHvp12TsQ-2zSFuv247xxhM9k7EIxWGKWCWJ-xv55kDBAE5BD4fr5yunia5WHCTB0E2So6JPzX_Zj" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="628" data-original-width="877" height="229" src="https://blogger.googleusercontent.com/img/a/AVvXsEgSpiJRVJ2oG6x9PnFKWX1Hq0_lctmOSzZrF2kMcVYecuxV9XS-N_blpD6vnN8VQJl9zwRia3psBEa8GEMqg71_jCiCRlFmEXbShlxmgcnY4q1wHvp12TsQ-2zSFuv247xxhM9k7EIxWGKWCWJ-xv55kDBAE5BD4fr5yunia5WHCTB0E2So6JPzX_Zj" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>I was able to fix this, though. I cut the traces between the pins, and soldered two bodge wires in my customary color for bidirectional serial -- (R)ed for (R)x from the point of view of the downstream device, and blue for Tx. I also added a pullup on the GPS_RESET line. With that it works.<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV5GfFpjwmagJefj2J1rKQJ53gTrzchpUrk6CNK8dnLmBo4nAakYrKi6qAGaZv41W6f5XvE0CjBYxnES8MzXe4DQfnq1VAOCqQgZIJ57qhtm4G-OASMX4XANLbBhkbUxh2lGX_OtM9GdZdZRZdyCsfJttq182I0dPS6I1VvfVHnnhsYKGCzK4BvOAE/s4032/20230416_100652.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1816" data-original-width="4032" height="144" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV5GfFpjwmagJefj2J1rKQJ53gTrzchpUrk6CNK8dnLmBo4nAakYrKi6qAGaZv41W6f5XvE0CjBYxnES8MzXe4DQfnq1VAOCqQgZIJ57qhtm4G-OASMX4XANLbBhkbUxh2lGX_OtM9GdZdZRZdyCsfJttq182I0dPS6I1VvfVHnnhsYKGCzK4BvOAE/s320/20230416_100652.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><p><br /><br /></p></div>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-22451808964072039672023-02-01T15:51:00.004-07:002023-02-01T16:03:07.282-07:00Jewel of the whenever: Matrix MultiplicationEigenchris (I kinda wish I thought of that first) has a series of videos on tensors. I can't give my opinion on the series yet, because I haven't seen them all yet. However, he does something cool that I have never seen before as a mnemonic for matrix multiplication (start at 4:32):<div>
<iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/bpuE_XmWQ8Y" width="320" youtube-src-id="bpuE_XmWQ8Y"></iframe></div><div><br /></div><div>We arrange the multiplication as follows. The final product goes in the bottom right. To the left of that, we put the left matrix, and *above* that we put the right matrix. Each cell is then the dot product of the row vector it is on and the column vector it is on</div><div><br /></div><div>\[\begin{matrix} & \begin{bmatrix} . & w_0 & . & . \\ . & w_1 & . & . \\ . & w_2 & . & . \\\end{bmatrix}\\ \begin{bmatrix} v_0 & v_1 & v_2 \\ . & . & \end{bmatrix}& \begin{bmatrix} . & \vec{v}\cdot\vec{w} & . & . \\ . & . & . & . \end{bmatrix}\end{matrix}\]</div><div><div><br /></div></div>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-81737408432797148312022-09-09T14:34:00.001-06:002022-09-09T14:34:30.592-06:00The One Picture that explains Phase Locked Loops<p> A Phase Locked Loop has always been mysterious to me until now. The following three pictures explain it all, and the third one is where the light goes on. Here are the first two, to build dramatic tension and also to do the best job I have ever seen of explaining the block diagram of a PLL. It's from Shawn Hymel's series on FPGA programming.</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/gmaSjyUij9E" width="320" youtube-src-id="gmaSjyUij9E"></iframe></div><br /><p></p><p>First diagram:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcU55rC6ERBeIspmtSyJsfvtY_mGxmxg57cHAi-gWKioNvV5w3RyxAZ8u3tz1JMjI-GAUZvxKma8znF037M8uHY6H_HK_QwI-0eiRrowM9-P7ZQOxdxRnAQf0LJWSA5ixzxc3kCCrag05AqGlyw_2YBpRexlbWb7jtoFajhNufpFuxPpc3OC7GcEsC/s1680/Screenshot%20from%202022-09-06%2011-25-42.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="783" data-original-width="1680" height="149" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcU55rC6ERBeIspmtSyJsfvtY_mGxmxg57cHAi-gWKioNvV5w3RyxAZ8u3tz1JMjI-GAUZvxKma8znF037M8uHY6H_HK_QwI-0eiRrowM9-P7ZQOxdxRnAQf0LJWSA5ixzxc3kCCrag05AqGlyw_2YBpRexlbWb7jtoFajhNufpFuxPpc3OC7GcEsC/s320/Screenshot%20from%202022-09-06%2011-25-42.png" width="320" /></a></div>The PLL consists of three sections:<div><ul style="text-align: left;"><li>The phase detector produces a signal based on whether the reference and fed back signals are in phase. I'm not sure of the details, but it might be something as simple as comparing both signals to zero (returning 1 if positive and 0 if negative) then XORing those comparisons. If the signals are in phase, they will always be on the same side of zero, and the phase detection output will be constant. If they are out of phase, sometimes they will be on opposite sides and the phase detection will not be constant.</li><li>The low-pass filter takes the phase detection signal, treats it as a PWM, and converts it to analog just by running it throug a resistor-capacitor (RC) circuit. The output is then some analog signal that is a function of the average of the phase detection signal.</li><li>The voltage-controlled oscilator (VCO) then takes that signal as an error signal. I'm sure it does some fancy PID magic, which finds just the right output signal to keep the input error signal at zero. It feeds this to the oscillator which then runs at the commanded frequency.</li><li>The output is fed back to the phase detector to produce a proper closed-loop control system.</li></ul><div>In this diagram, the output of the VCO is significantly out of phase with the reference, *because* it is not the right frequency. It's impossible for two signals of different frequency to stay in phase.</div><div><br /></div><div>Second diagram:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEvDfU1gQHH_PL71U4ZpDMgVgz3vguN6DKxRsKueOjFkX4WgzPa98YduPLWuU33Cift8E-sRbMbQtvEh49hF-7IBc7ViDSIsRubhOuYNK9oGRU4Ee967HyUJw1AytV6fakrk7rgzc773Fh_vcOKa_hdvtonQeqo7t6TfjxcA7ZzDBMmjvXIK5IS54v/s1697/Screenshot%20from%202022-09-06%2011-26-04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="802" data-original-width="1697" height="151" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEvDfU1gQHH_PL71U4ZpDMgVgz3vguN6DKxRsKueOjFkX4WgzPa98YduPLWuU33Cift8E-sRbMbQtvEh49hF-7IBc7ViDSIsRubhOuYNK9oGRU4Ee967HyUJw1AytV6fakrk7rgzc773Fh_vcOKa_hdvtonQeqo7t6TfjxcA7ZzDBMmjvXIK5IS54v/s320/Screenshot%20from%202022-09-06%2011-26-04.png" width="320" /></a></div><br /><div>In this diagram, the output phase has locked. The error signal from the phase detector and LPF is zero, and the controller in the VCO knows that whatever settings it is using now are correct, and keeps them there. (Note that in this diagram, the clock is an analog sine wave. Just pretend it's a digital square wave.)</div><p>Third diagram, and critical part:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSEYILS5lCCipyxowWd1-FmmnFoZ1ax4Z3B9DSIC_qkXdSW_1VWOstAsWtmiDt8ernPwQ_DrO9S3UnxawNkOmzINAN9uXOmRAwu8QqWTaO_O65WnHvSZ8UjVg-CmlUSxjSXumdI-BR8XDK8bpOdNDYkh3VoJKi01QGNre1uTwfrfCh7nsUNXDyGeRg/s1671/Screenshot%20from%202022-09-06%2011-25-04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="865" data-original-width="1671" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSEYILS5lCCipyxowWd1-FmmnFoZ1ax4Z3B9DSIC_qkXdSW_1VWOstAsWtmiDt8ernPwQ_DrO9S3UnxawNkOmzINAN9uXOmRAwu8QqWTaO_O65WnHvSZ8UjVg-CmlUSxjSXumdI-BR8XDK8bpOdNDYkh3VoJKi01QGNre1uTwfrfCh7nsUNXDyGeRg/s320/Screenshot%20from%202022-09-06%2011-25-04.png" width="320" /></a></div><br /><p>This shows a clock divider in the feedback part. Digital clock dividers are relatively easy to implement, requiring a counter. To divide by N, make a counter big enough to count to N. Each input clock, increment the counter, but when the counter is about to reach N, reset it instead. If N is even, then it's pretty easy to set up some logic so that whenever the counter is in the first half of its run, a low signal is output, and vice versa. Odd is a little bit trickier, but still doable.</p><p>Multipliers on the other hand are difficult, and in fact are why we need all this fancy PLL stuff to begin with. With a PLL and a *divider* in the feedback path, we can implement a *multiplier*.</p><p>If you put a divider on the input reference signal as well, you can get frequency multiplication by any rational factor.</p></div>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-8648389546463424292022-08-16T18:22:00.003-06:002022-08-16T18:25:12.300-06:00Shipometer<p> Having given up on #SoME2, it's time to move on to the next project. Next week I will be going on a cruise. On the cruise, and also on the plane, I wish to record GPS signals. The pocketometer has all the right sensors for that, but unfortunately is not sufficiently reliable. The Raspberry Pi has already proven itself capable of recording GPS from one of the ZED-F9R breakout boards. Now the question is if it can record the sensors on I2C, and the time pulse.</p><p>I want to see if I can do this with just the parts that I already have on my desk.</p><p>I have a belt bag big enough to hold all the sensors, the Pi, and a 20Ah USB battery pack. That would be a lot less suspicious than stuffing stuff in my pocket.</p><p>It would also be great if the Pi could act like a wifi hotspot and serve SSH through it. That way I could look at it on the phone while (literally) in flight.</p><p>The last thing that would be awesome is timer capture on the GPIO, of at least the PPS and maybe others, like the interrupt lines from the sensors. If it can't, maybe we could get a program on the Teensy that would do the timer capture and output on UART or as an I2C slave.</p><p><br /></p>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-16450427508250465002022-08-16T18:12:00.004-06:002022-08-16T18:12:43.650-06:00#SoME2 post-mortem<p> I did not get a video out on time for SoME2. Even for just the descoped "good part" video, I couldn't get it done in time this morning.</p><p><br /></p>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-39944293297168878852022-08-11T11:33:00.004-06:002022-08-11T11:40:48.853-06:00Deadline pressure<p>I ended up going with the Kalman gain video. I will use this blog as "making of" documentation.</p><p></p><ul style="text-align: left;"><li>I am going to use PictureBox much more than Manim -- I have already forgotten how to use Manim. I think I will only need it for dancing equations, and I don't plan on using those much. MatPlotLib knows how to use TeX, so it can make nice-looking math, but can't make them dance as well.</li><li>One video, two videos, N videos? I have about 9 minutes of narration so far, and have just covered up to measurement space. It might take 20 minutes to cover the stuff I want just for linear Kalman filtering. KalmanGain implies that the most important or interesting stuff is calculating the gain. That by itself might only take a minute, but all of the pre-requisites might take even more than 20 minutes. For now, I am planning on one long video, covering transformation of uncertainty from state to measurement space and back, and the hand-waving justification for why there even is an optimal gain matrix to transform innovations back into state vector adjustments.</li><li>Pure video, or text plus video? This interacts with above. The hard thing about videos, is that the maker of the video always has to leave stuff on the cutting room floor. This leads to a contradiction: If I include everything I know, the video will be too long and boring, while if I don't, the pedants in the audience will use this as an opportunity to show how much smarter they are than me. Text plus video would allow me to put the most important bits in a video, which could be run as a continuous playlist. The text part would then include the videos, and footnotes with all the pedantry included.</li><li>Narration. I hate the sound of my recorded voice. Plus, I don't have a good audio setup yet. Therefore, I am using Amazon Polly to generate narration from a script. I am having neural British Amy read it. This is the first text-to-speech I have heard that I would say is good enough to be plausible as a human-read narration. I would say it's 80% of the way there. I just wish I could adjust the emphasis on some words. American Joanna is also good enough, but as an American, I at least subconsciously buy into the "intelligent British accent" stereotype. I know that I write slightly different for Amy than I would for myself, and a lot different than I do on this blog.</li></ul><div>Which brings us to deadline pressure. SoME2 was announced in the middle of June, but I didn't start on it in earnest until this past Monday (August 11). If I had all the time in the world, I would make a video covering time updating, nonlinear models, and maybe various filters beyond Extended Kalman. It might take an hour, but I would break it up into roughly 20-minute chapters. With the deadline pressure, I am not going to be able to carry out this whole program. I might be limited to just the gain matrix.</div><div><br /></div><div>I am *not* going to do things like publish one chapter before the #SoME2 deadline and extend a playlist afterwards. I am committing to publishing a complete thought, or nothing at all.</div><div><br /></div><div>Why is #SoME2 even important? For one thing, I don't expect to win. This might be a winning concept in the right hands, but I am learning that my hands are not those. I am also learning why 3b1b only publishes as infrequently as he does. That's the non-reason, so the reason is: I like having large numbers in my YouTube channel. My last-years late #SoME1 got 104,000 views, even though it didn't make the official #SoME1 playlist.</div><div><br /></div><div>Also, without deadline pressure, I may never actually make this. One of the issues with my projects is that whenever I am working on one, I wish I was working on another. I'm sure that's a common character flaw among humans in general, but I don't know what it's called or how to fight it.</div><p></p><p><br /></p>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-83599364082959576002022-06-09T00:08:00.000-06:002022-06-09T00:08:09.266-06:00Decisions, Decisions<p>Summer of Math Exposition #2 was finally announced. Deadline is August 15, 2022, which fits in quite well with my summer plans -- it's all in between the Florida trip and the other Florida trip.</p><p>I could do the next logical step from Exponential beats All. This one was supposed to be a quick explainer for my real video idea, why a rocket doesn't need a heat shield.</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/LkPkf5vJ6Mw" width="320" youtube-src-id="LkPkf5vJ6Mw"></iframe></div><br /><p></p><p>Or, I could do a visualization of the Kalman filter. There are of course lots of interesting visualizations to do, but the one I am interested in is showing off the Kalman gain, as the gain matrix which is a linear unbiased estimator, and demonstrating that picking other values for the gain will necessarily produce larger covariance.</p><p>Eventually I plan on doing both, but I am leaning towards the "why doesn't a rocket need a heat shield" video. It's more physics and less math, but "math" is given an especially wide breadth in Some2.</p>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-40194008198577948872022-04-29T13:06:00.002-06:002022-04-29T13:06:23.315-06:00A better C++ than C++?<p>tl;dr -- Is Rust the language that C++ promised but failed to deliver? It's still unclear.</p><p>Programmers are adults. We don't need our hands held, and we certainly don't need to be told "Don't do that!" The only kind of advice that is needed is "I see what you are *trying* to do. This might be a better way to do it..."</p><p>Case in point: reading data from external sensors. I am still working on the rollercoasterometer (now for 13 glorious years!) and am doing my usual fighting with C++ about getting formatted data out of a byte buffer. Back in the bad old days, we would cast the address of the buffer as a pointer to a struct, and read the data out directly. Then the compiler writers, with their fancy-smancy alignment and such, said to not do that, because there is no guarantee that the structure will line up with what you think. The compiler is free to put any amount of padding between fields, order the fields however it likes, put invisible fields like vptr, etc. </p><p>That by itself isn't bad. The bad part is when I ask how to do what I want, and the answer comes back: "Don't". There is no portable way to guarantee that any field in a struct lands anywhere. This way we can write C++ targeting the Apollo Guidance Computer, with its 15-bit words, no such concept as "byte", ones-complement arithmetic with +0 and -0, etc. It doesn't matter that basically every machine in the last 40 years has been twos complement, 8-bit bytes, and word size of a power of 2 bytes. Basically the only disagreement is endian-ness. </p><p>But, C++ won't let me take advantage of the fact that both machines in a transaction have the same native word format. No, I have to individually extract each byte, shift and OR it myself, etc, to get the data out. If I am lucky, the compiler will see that I am translating from English to English, and optimize it out.</p><p>Game designers learned long ago to learn from their users. If all the Minecraft users are building farms, then support the building of farms. If the farm depends on a glitch, consider formalizing the glitch and making it an official feature. Don't just shower them with whatever they are farming for "free", but don't take away their ability to farm either. For instance, Mojang has considered in several instances changing the mechanics of how villagers and iron golems work to discourage farming iron. They got quite a bit of pushback from the community, and have therefore backed off. They don't "support" iron farming, but they haven't removed it either.</p><p>The C++ committee on the other hand seems to be driven by two factors:</p><p></p><ol style="text-align: left;"><li>Backward compatibility indefinitely into the past</li><li>Ability of compiler writers to game benchmarks</li></ol><div>C++ has a lot of good ideas (some might say too many) but it is a language which has evolved for decades while at the same time hasn't been able to shed old, bad ideas or old, bad implementations of good ideas. </div><div><br /></div><div>C++ for whatever reason also has a burning hatred for the preprocessor. The preprocessor and compiler are married, but they are both trapped in a loveless marriage, and the simmering hatred has boiled over to the point where the official C++ FAQ considers macros to be "evil". Now the preprocessor does have some minuses, mainly in type checking. So, we were given constants and templates. We were told that templates would basically eliminate the need for macros. However, when we tried to use them like that, we found that the promise has not quite been kept. Templates do some but not all of the compile-time processing that we want. Constexpr functions are helping, but aren't there yet. </div><div><br /></div><div>My use case is that I want to make a self-documenting logger. The rollercoasterometer reads a bunch of sensors, formats the data into packets, then writes the packets to a file on the SD card. Since the data from the sensors doesn't naturally come in packets, and doesn't naturally have a timestamp, the main firmware timestamps the data and formats it into packets. Since I write the firmware, I also have to write the code on the host which interprets the packets. I came up with a [[clever idea]] to have the program emit a series of packet-documentation packets as it starts up. One way to do it is to have the program create a documentation packet the first time it emits each packet, and I have done this. It looks something like this:</div><div><br /></div><div>void start_packet(apid, packet_desc_str) {</div><div> if apid is not yet documented:</div><div> write a documentation packet for this packet, using the packet_desc_str</div><div> start the packet, perhaps to a backup buffer if we are documenting the packet</div><div>}</div><div>void fill<type>(data_value, field_desc_str) {</div><div> if apid is not yet documented:</div><div> write a documentation packer for this field, using the field_desc_str</div><div> write the field to the packet, perhaps to the backup buffer</div><div>}</div><div>void finish(apid) {</div><div> if the apid is being documented:</div><div> take note that we have documented it and don't do it next time</div><div> finish the packet and write it, perhaps from the backup buffer</div><div>}</div><div><br /></div><div>Note that we need to have two buffers. It would be far better if we did something like this:</div><div><br /></div><div>void start_packet(apid,packet_desc_str) {</div><div> compile_time_code {</div><div> create a packet describing this packet in the packet description block. This block will be a read-only blob as far as the runtime code is concerned</div><div> }</div><div> start the packet, no need for backup buffer</div><div>}</div><div>void fill<type>(data_value,field_desc_str) {</div><div> compile_time_code {</div><div> Add a packet describing this field to the packet description block</div><div> }</div><div> add the field to the packet</div><div>}</div><div>void finish() {</div><div> finish the packet and write it</div><div>}</div><div>void setup() {</div><div> open sd card</div><div> write packet description block</div><div> set up sensors etc</div><div>}</div><div>void loop() {</div><div> read sensor</div><div> start_packet(0x1234,"sensor packet");</div><div> fillu16(value_from_sensor,"value from sensor")</div><div> ...</div><div> finish_packet()</div><div>}</div><div><br /></div><div>Preprocessor macros might be able to do it, but the preprocessor is Evil. Templates might be able to do it, but it might require template metaprogramming, which is actually evil. It seems like there isn't a way to do it in C++, certainly not a clean way. Therefore we are forced to either use the official preprocessor, write our own preprocessor (which has its own headaches), or do it at runtime.</div><div><br /></div><div>The language which might be a better C++ than C++ is Rust. This is a statically typed language which is designed to be compiled into good machine code (the reference compiler uses LLVM as its back end) but with some features added and some taken away. I'm not sure if I like mutability and ownership yet, and haven't gotten used to the concept of borrowing yet, but it does look like there is support for forcing a struct to land on certain bytes, and it does look like (with procedural macros) there might be enough compiler support for compile-time computation.</div><div><br /></div><div>To test this out, I am going to work on three projects:</div><div><ol style="text-align: left;"><li>A conversion of kwantrace to Rust, to experiment with plain application-domain programming.</li><li>A packet parser for reading rollercoasterometer logs</li><li>Firmware for the rollercoasterometer</li></ol><div>The last one is probably not going to be ready in time for my next expedition to a roller coaster.</div></div><p></p>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-41669928952150491782022-02-01T15:40:00.004-07:002022-02-02T20:42:23.176-07:00Babbage's Dream<p> I just got a pair of 512GB MicroSD cards. Of course, any such card should be tested, since these are about the easiest things in the world to counterfeit and it can be done in a way which normally wouldn't be detected for a while.</p><p>For instance, imagine you wanted to fake a 512GB card. If it was inert plastic, it would immediately fail and the user would dispute the transaction. So instead, you make a card with less capacity (say 32GB) and reprogram it to do the following:</p><p></p><ul style="text-align: left;"><li>Report 512GB of capacity</li><li>Whenever doing a read or write, take the block address given and just mod it with the actual capacity. </li></ul>So if you write data to block 0, then to the block at 32GB, the later block would overwrite the first block. This is less than ideal for a storage device... It would work fine until you wrote 33GB on it, which might take a while.<p></p><p>Having said this, there are very many MicroSD cards which are counterfeit in this manner. It behooves us then to test each card immediately.</p><p>You can't just test it by writing zeros, as the card may already have zeros on it. You can't just write a fixed pattern (say 0xAA) because a smart enough counterfeit (and note that MicroSD cards have a full-blown microcontroller with its own firmware) would notice this and read back the pattern. You can't just test a small amount of it, because the card (or host) may have a cache. </p><p>So, I have put together a program which writes the output of a cryptographically-secure random number generator to stdout. This can be directed to a file on the SD card to be tested. The chosen CSPRNG is the Keccak-1600 sponge function. We absorb any arbitrary string as the seed, then keep squeezing it forever, or until the device fills up and throws an error message. </p><p>There is no way to beat this, since a CSPRNG by its nature is unpredictable -- there is no detectable pattern unless you know the arbitrary string used as the seed. The internal microcontroller probably <i>could</i> calculate it, since Keccak is a well-known standard alogrithm, but it doesn't know the seed. It's not reasonable for the counterfeiters to guess that I am going to be checking the chip this way, and to try to guess the seed (hint -- it's close to a substring of the title of this blog).</p><p>There is no way to derive the seed from the output of the CSPRNG -- that's one of the properties that makes the PRNG cryptographically secure. The stream might have a period of 2^1600 bits, but I haven't seen a proof or even any good reason to believe that Keccak is full-period when used this way. Even if it is much less, it is almost certainly much greater than the 2^42 bits it takes to fill this device. So there is no way to store or cache this stream without actually having a functional amount of storage equal to the advertised amount.</p><p>In my case, I couldn't find the exact routine I wanted, so I wrote my own based on the XKCP package (since it isn't wise to implement a cryptographic primitive yourself).</p><p><span style="font-family: courier;"></span></p><blockquote><p><span style="font-family: courier;">#include "KeccakSponge.h"</span></p><p><span style="font-family: courier;">#include "stdio.h"</span></p><p><span style="font-family: courier;"><br /></span></p><p><span style="font-family: courier;">#define r 576</span></p><p><span style="font-family: courier;">#define c (1600-r)</span></p><p><span style="font-family: courier;"><br /></span></p><p><span style="font-family: courier;">int main(int argc, const unsigned char** argv) {</span></p><p><span style="font-family: courier;"> KeccakWidth1600_SpongeInstance s;</span></p><p><span style="font-family: courier;"> KeccakWidth1600_SpongeInitialize(&s,r,c);</span></p><p><span style="font-family: courier;"> char buf[r/8];</span></p><p><span style="font-family: courier;"> KeccakWidth1600_SpongeAbsorb(&s,argv[1],strlen(argv[1]));</span></p><p><span style="font-family: courier;"> if(argc==2) {</span></p><p><span style="font-family: courier;"> for(;;) {</span></p><p><span style="font-family: courier;"> KeccakWidth1600_SpongeSqueeze(&s,buf,sizeof(buf));</span></p><p><span style="font-family: courier;"> fwrite(buf,1,sizeof(buf),stdout);</span></p><p><span style="font-family: courier;"> }</span></p><p><span style="font-family: courier;"> } else {</span></p><p><span style="font-family: courier;"> FILE* inf=fopen(argv[2],"rb");</span></p><p><span style="font-family: courier;"> char inbuf[r/8];</span></p><p><span style="font-family: courier;"> size_t last_match=0;</span></p><p><span style="font-family: courier;"> while(!feof(inf)) {</span></p><p><span style="font-family: courier;"> KeccakWidth1600_SpongeSqueeze(&s,buf,sizeof(buf));</span></p><p><span style="font-family: courier;"> size_t incount=fread(inbuf,1,sizeof(inbuf),inf);</span></p><p><span style="font-family: courier;"> for(size_t i=0;i<incount;i++) {</span></p><p><span style="font-family: courier;"> if(buf[i]!=inbuf[i]) {</span></p><p><span style="font-family: courier;"> fclose(inf);</span></p><p><span style="font-family: courier;"> printf("Different at byte %ld\n",last_match);</span></p><p><span style="font-family: courier;"> return 1;</span></p><p><span style="font-family: courier;"> }</span></p><p><span style="font-family: courier;"> last_match++;</span></p><p><span style="font-family: courier;"> if ((last_match%(1024*1024*32))==0) {</span></p><p><span style="font-family: courier;"> printf(".");</span></p><p><span style="font-family: courier;"> if((last_match%(1024*1024*1024))==0) {</span></p><p><span style="font-family: courier;"><span style="white-space: pre;"> </span> printf("%ld\n",last_match/(1024*1024*1024));</span></p><p><span style="font-family: courier;"><span style="white-space: pre;"> </span> }</span></p><p><span style="font-family: courier;"> fflush(stdout);</span></p><p><span style="font-family: courier;"> }</span></p><p><span style="font-family: courier;"> }</span></p><p><span style="font-family: courier;"> }</span></p><p><span style="font-family: courier;"> fclose(inf);</span></p><p><span style="font-family: courier;"> printf("All bytes up to %ld matched",last_match);</span></p><p><span style="font-family: courier;"> return 0;</span></p><p><span style="font-family: courier;"> }</span></p><p><span style="font-family: courier;">}</span></p></blockquote><p>This program takes one or two strings as command-line arguments. The first is the seed. If there is only one argument, it uses this seed as mentioned above to absorb, then squeezes the sponge an unlimited amount of times, limited only by the device filling up. It writes to stdout, so the way I use it is to pipe it through pv to see how fast it is going, and then pipe it to a file on the device under test. If the card is genuine, then this test is non-destructive. It also doesn't disturb the original exFAT filesystem formatting that the card came with -- I have heard that there is a non-obvious optimum way to format these cards, and that they come formatted optimally.</p><p>If there are two arguments, the first is still the seed, and the second is a file to check to see if it matches. It does this in the simplest way possible, by absorbing the seed, then going into a loop: squeezing once, reading the same amount from the file, and byte-for-byte checking the blocks. It prints a period every 32MiB and a number every 1024MiB. It prints the byte offset of the first non-match (and then exits) or prints the total number of bytes it checked.</p><p>As it happens, my chip worked properly. This means the system produces a stream of over 4 TRILLION bits, then perfectly reproduces those 4 TRILLION bits. That's amazing when you think about it, and is Babbage's dream. The legend goes that after being disillusioned by the number of errors in an almanac, he remarked that he wishes that the tables could be generated by steam power. One of his colleagues said "that <i>is</i> possible", which statement changed the course of Babbage's life. He originally wanted to use the difference engine and analytical engine to literally crank out mathematical and almanac tables, with no human intervention between the initial conditions and the printed page. The machine was intended to perform the calculation, then with the results, automatically make a plate to be used in a printing press. Many years later, the first and simplest such difference engine was finally constructed, and printed the first several integer multiples of the circle constant pi. <i>It made a mistake in the last entry.</i> </p><p><span style="font-family: courier;"></span></p>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-40343315637814106212021-02-02T10:27:00.005-07:002021-02-02T22:38:11.739-07:00Dusting off some old code<p> In preparation for the Mars Science laboratory landing, I made a video using the best pre-EDL data available, including a simulated EDL kernel set, MOLA topography, and HiRISE imaging. I'm pretty proud of it:</p><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/T2Ynu8sms7g" width="320" youtube-src-id="T2Ynu8sms7g"></iframe></div><br /><p><span></span></p><a name='more'></a>A couple of things that can be improved:<p></p><p></p><ul style="text-align: left;"><li>NOT the obvious border between different photo areas. I like that effect, and it makes it harder for others to pass it off as anything other than a simulation</li><li>Proper drop-off of the entry balance masses</li><li>A couple of editing details here and there</li><li>Better display of the landing ellipse</li></ul><div>Things which will be stolen as-is:</div><div><ul style="text-align: left;"><li>Model of the lander, entry capsule, and parachute</li><li>Flame and scorch effect</li></ul></div><div>Available data:</div><div><ul style="text-align: left;"><li>Current best simulated trajectory is believed to be <a href="https://naif.jpl.nasa.gov/pub/naif/MARS2020/kernels/spk/m2020_trajCEDLS-6DOF_ops_od020v1_AL23.bsp" rel="nofollow">this kernel covering cruise, EDL, and on-surface</a> at the landing site</li><li>No attitude data yet. It might be possible to simulate it myself, based on how MSL flew.</li><li>Solar system geometry from <a href="https://naif.jpl.nasa.gov/pub/naif/generic_kernels/" rel="nofollow">NAIF</a>, including DE430 and MAR097 Solar System geometry.</li><li>NASA public-facing <a href="https://mars.nasa.gov/maps/location/?mission=M20">website</a> showing the ground track of the rover. Before landing, it only shows the landing ellipse. This has image map data down to ~1m resolution around the landing site, in an extractable format.</li><li>Mars Global Surveyor MOLA data -- the stuff we want is radii, at 128 pixels per degree.</li><li>Landing ellipse -- The given landing ellipse has a major radius of 4.24km, a minor radius of 4.00km, and a major-axis azimuth indistinguishable from due east. I believe this to be the 99% confidence curve, which I think is about 3-sigma.</li></ul><div>A lot of the old code depended on KwanPov (then called AstroPov). Unfortunately that version was lost in the Great Hard Drive Crash of 2012. Therefore I will be using modern KwanPov, which has the usual Spice stuff and double-precision variables, but not the old tile map stuff.</div></div><div><br /></div><div>So, what will we be using instead? </div><h1 style="text-align: left;">Topography</h1><div>For topography, we used to use mesh2 objects. Now, we will use rectangular height fields. There is quite a bit of optimization in a height-field that we can't take advantage of with a regular mesh:</div><div><ul style="text-align: left;"><li>Height fields can use images as their base data, while meshes require text, which takes much longer to load.</li><li>Height fields are smaller and use less memory and storage.</li><li>Height fields are optimized -- imagine each height field is a cityscape, where each pixel is a skyscraper. When tracing a ray, the path of the ray across the height field can be projected down to two dimensions, and only those pixels in the height field which are crossed by the ray need to be checked in full 3D. </li></ul></div><div>The minus side is that this will take quite a bit of pre-processing through something like Python (with Numpy and Scipy). The MOLA data will be resampled on a 3D cartesian grid. We will rotate the globe so that the part we want is on top. We will grab each section in a square aligned to the XY grid, and then use the Z coordinate as the value of the height field. Since you can't tessellate a disco ball with squares without having gaps, we won't try -- we will use the same rotation for all tiles. The square borders will fall along constant X and Y, not constant latitude and longitude.</div><div><br /></div><div>We will sample each tile so that the full range of Z in that tile is mapped to [0..255], round Z off to the nearest integer, then record the X, Y, and Z bounding box so that the height field can be appropriately translated and scaled to fit back into the world. I'll use 8-bit resolution until it proves unsuitable, because there are many more tools to work with 8-bit grayscale PNG images than 16-bit.</div><div><br /></div><h1 style="text-align: left;">Image map</h1><div>For image maps, we can use the ill-documented <span style="font-family: courier;">repeat</span> and <span style="font-family: courier;">offset</span> keywords in an image map, along with the better-documented <span style="font-family: courier;">once</span> keyword. This will be done as a layered texture, something like this:</div><div><br /></div><div style="text-align: left;"><span style="font-family: courier;">object {</span></div><div style="text-align: left;"><span style="font-family: courier;"> blah blah blah </span></div><div style="text-align: left;"><span style="font-family: courier;"> texture {</span></div><div style="text-align: left;"><span style="font-family: courier;"> pigment {</span></div><div style="text-align: left;"><span style="font-family: courier;"> image_map {</span></div><div style="text-align: left;"><span style="font-family: courier;"> png "tile03x04.png" </span></div><div style="text-align: left;"><span style="font-family: courier;"> map_type spherical </span></div><div style="text-align: left;"><span style="font-family: courier;"> <b>once repeat <tiles_x,tiles_y> offset <3,4></b></span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><div><span style="font-family: courier;"> texture {</span></div><div><span style="font-family: courier;"> pigment {</span></div><div><span style="font-family: courier;"> image_map {</span></div><div><span style="font-family: courier;"> png "tile03x05.png" </span></div><div><span style="font-family: courier;"> map_type spherical </span></div><div><span style="font-family: courier;"> <b>once repeat <tiles_x,tiles_y> offset <3,5></b></span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: inherit;">The repeat function does what it seems -- causes the given image to be scaled so that it can repeat so many times in the U and V direction. Either of these repeat components can be fractional, so you can do things like repeat 0.5 times (only use half the image), 1.5 times, etc.</span></div><div><span style="font-family: inherit;"><br /></span></div><div><span style="font-family: inherit;">Similarly, the offset function does what it seems -- causes the given image to be moved around image space. If the image actually repeats, it doesn't make much sense for the offset to be out of range of [0,1) but if it is used only once, then any values can be used for U and V.</span></div><div><span style="font-family: inherit;"><br /></span></div><div><span style="font-family: inherit;">I think order makes a difference, so if you repeat first then offset, you get a different result than if you offset then repeat.</span></div><div><span style="font-family: inherit;"><br /></span></div><div><span style="font-family: inherit;">The once keyword means that the image is used only once, rather than tiling the whole UV space. If you check the point of a texture that is "outside" the original image, texture calculation is immediately aborted and this layer is treated as completely transparent.</span></div><div><span style="font-family: inherit;"><br /></span></div><div><span style="font-family: inherit;">We take advantage of layered textures. If more than one texture is specified for an object, and the top texture is transparent at the given point, then the next texture is checked, and so on. I have no idea how efficient it is to do hundreds or thousands of layers, but I don't think there is any issue other than efficiency.</span></div><div><span style="font-family: inherit;"><br /></span></div><h1 style="text-align: left;"><span style="font-family: inherit;">GIS data</span></h1><div><span style="font-family: inherit;">Data from the GIS appears to be as follows:</span></div><div><span style="font-family: inherit;"><br /></span></div><div><ul style="text-align: left;"><li><span style="font-family: inherit;">Structured in layers, where each layer is exactly twice as zoomed in as the previous.</span></li><li><span style="font-family: inherit;">The highest-resolution dataset goes up to level 18</span></li><li><span style="font-family: inherit;">There are two base maps -- "Basemap" which appears to be derived from especially-high-resolution, targeted images from HiRISE, and "North East Syrtis Base Map" which is distinctly lower resolution, only going up to level 12.</span></li><li>The data is structured as level/column/row, where column strictly increases with east longitude, and row increases with north latitude.</li><li>The level is broken up into 256-pixel tiles.</li></ul><div>Establishing the exact scale will take some doing. On level 18:</div></div><div><ul style="text-align: left;"><li>There is a black border around the data</li><li>The corner of the light data is visible and can have coordinates measured. <div class="separator" style="clear: both; text-align: center;"><br /></div><br /></li><li>The lower-left darkest-but-not-black corner is in tile 187259/144561, at pixel column 30, row 12 (from top). This is at 77.16126356273891E, 18.21102425519005N</li><li>The upper-left about 50%-black corner is in tile 187258/144593, at pixel column 137, row 99 (from top). This is at 77.16046560555698E, 18.721192572014488N</li><li>The upper-right not-quite-black corner is in tile 187650/144953, at pixel [xxx,xxx], at location 77.69832998514177E,18.7211973350726N</li><li>The lower-right not-quite-black corner is in tile 187650/144561, at pixel [xxx,xxx], at location 77.69832998514177E,18.211024892155642N</li><li>On a spherical surface of radius 3396km, 1 degree is 59.271km, so 1 millidegree is 59.271m and one microdegree is 59.271mm.</li><li>At level 18, one pixel at the bottom edge of the basemap spans about 77.69832730293275-77.69833266735078=5.3 microdegrees, or about 318mm.</li></ul><div>It looks like there are 262144=2^18 tiles across, which makes perfect sense for being level 18. The leftmost column has its left edge at longitude -180deg. The latitude isn't coming out quite so cleanly.</div></div></div><p></p>kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-60506995217187121802019-10-30T13:26:00.000-06:002019-10-30T14:39:10.644-06:00Palpatine was DeadPalpatine was dead: to
begin with. There is no doubt whatever about that. His flailing body was flung down the open reactor shaft. Crackling with dark side power, he fell faster and faster until he hit a bridge far down the shaft, and burst like a bomb. Anakin and Luke felt the concussion even hundreds of meters away. And if that wasn't enough, he was killed twice, once by his former apprentice, and once by the terrorists who destroyed the planet-sized station that the pieces of his body were at rest upon. Anakin saw it, Luke saw it, we in the audience saw it.<br />
<br />
There is no doubt that Palpatine was dead. This must be
distinctly understood, or nothing wonderful can come of the story I am
going to relate....kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-46062157681500938752019-10-23T15:54:00.000-06:002019-10-30T13:32:06.621-06:00What makes a story?<blockquote class="tr_bq">
<i>"I wonder what they'll be like?" he mused. "Will they be nothing but
wonderful engineers, with no art or philosophy?"</i> --From <i>Rescue Party</i> by Arthur C. Clarke</blockquote>
<blockquote class="tr_bq">
<i>Specialization is for insects</i> --Attributed to Robert A. Heinlein </blockquote>
<br />
I might think of myself as an engineer (and by no means wonderful) but sometimes I think about art and philosophy as well. Today over lunch I was thinking about what makes a great story -- what makes a story entertaining to me? For instance, why do I like Star Wars? Is it the characters? Is it the message?<br />
<br />
For me, the most entertaining part of the original trilogy was the Battle of Endor (which the Empire totally should have won, by the way. Ewoks?). Specifically the best part of that was the run to the Death Star core. Why? Great visuals. We got to see the intricate detail of the inside of a massive, complicated object. The interior of the Death Star is itself a work of art.<br />
<br />
Similarly, the boarding and launch of USS Enterprise in Star Trek: The Motion(less) Picture is the best part of that film by far. The new Star Trek had its moments, but they were too fast and filmed in shaky-vision such that we never got a clean look at all of the great models. From the clips I have seen of Star Trek: Beyond, it looks like Starbase Yorktown was done right. It might be way too large and expensive to be practical, but it does look awesome. <br />
<br />
So, why aren't all movies just spectacular visual effects?<br />
<br />
Firstly, visual effects are not cheap. It is far cheaper per minute to put a bunch of actors on a sound stage and just record a play, compared to special effects.<br />
<br />
Second, if we do, we end up with such works as Sonic Vision and The Mind's Eye. These are works of art in themselves, but there is still something missing. I don't think even I could watch Sonic Vision for two straight hours. I finally think I know what it is. It's <i>consistency</i>. In a well-constructed story, all the pieces just fit together. As long as it maintains consistency, the larger the story, the better. In such a story, you can understand what is going on. You can make predictions, and evaluate the actions of the characters. Did it make sense for Han Solo to do that? Did it make sense for Admiral Holdo to do that? Harry Potter seems consistent, and it maintained that through seven books.<br />
<br />
A good consistent story, then it seems, must be well-planned from the start. A good story universe, must have a solid scaffold of ideas, and all new ideas added to it must remain consistent. The best ideas might expand the scaffold, but in a way that makes it stronger and able to hold even more ideas. The core of any good story universe is one good story.<br />
<br />
So: Why <i>did</i> Admiral Holdo do that? It was a visually stunning 10 seconds, but how does it work as for consistency? In order for it to make sense, she must have had some idea that it could work -- not necessarily a sure thing, but at least a reasonable chance to be worth trading her life for. If hyperspace ramming works, then why isn't it always used?<br />
<br />
Also, the Holdo Maneuver didn't even get a mission kill -- <i>Supremacy</i> was not destroyed, only damaged. It was not stopped, only slowed. It still was able to launch an invasion of Crait. She aimed for a wing, rather than the core.<br />
<br />
Here are the facts as shown in <i>The Last Jedi</i>:<br />
<ul>
<li><i>Raddus</i> was almost out of fuel -- it had enough for one jump, and no further fuel to travel through normal space. </li>
<li>Admiral Holdo ordered the abandonment of <i>Raddus</i> with her alone staying aboard. </li>
<li>She turned the ship to face <i>Supremacy</i>.</li>
<li>The crew of <i>Supremacy</i> thought that <i>Raddus</i> was either trying to escape or was trying to block/stall to give the rest of the fleet a chance to escape.</li>
<li>Just before <i>Raddus</i> jumped to hyperspace, the crew of <i>Supremacy</i> realized what <i>Raddus</i> was trying to do, and started to take action to counter, but they no longer had enough time to prevent it.</li>
<li><i>Raddus</i> jumped to hyperspace with its trajectory through the right wing of <i>Supremacy</i>. It isn't clear from the footage whether <i>Raddus</i> actually entered hyperspace, or just hit <i>Supremacy</i> at high speed in normal space. In any case, the right wing of <i>Supremacy</i> was sheared off, and at least four trailing ships were destroyed by debris from the collision.</li>
</ul>
Other facts seen in other parts of canon:<br />
<ul>
<li>During Rogue One, <i>Devastator</i> jumps into Scarif orbit just as the rebel fleet is trying to flee. At least one GR75 (transport-class) ship hits <i>Devastator's</i> hull and is destroyed, and its pieces are just brushed off.</li>
<li>In one of the Legends comic books, a squadron of three star destroyers drops out of hyperspace right on top of <i>Executor</i>. All three ships are smashed to atoms, and while <i>Executor</i> has to stop what it is doing, its shields brush the collision aside, such that the paint isn't even scratched.</li>
</ul>
<br />
We can enumerate all the possibilities, and dismiss each of them. The Holdo Maneuver is inconsistent with what has come before.<br />
<br />
<ul>
<li>Admiral Holdo is such a great military leader that this idea is original to her. In the twenty thousand year history of the galaxy, no one else has had this idea. We dismiss this because even a cursory study of either galactic or Earth history would have revealed many examples of ramming as a reasonable tactic. I have seen Youtube videos by one of the numberless online jabberers stating exactly this, before <i>The Last Jedi</i> was released, as an alternate way to destroy the Death Star, use an X-Wing in kamikaze mode.</li>
<li>The shields of <i>Supremacy</i> should have been able to protect it from the collision, as seen in other hyperspace-shield interactions. Admiral Holdo should have known this and not even have attempted the ram.</li>
<li>You can't just jump one X-wing to hyperspace and expect to destroy the Death Star -- a single X-wing isn't big enough. Anything bigger is supposedly too expensive. However, if just mass will do the trick, there is enough plain mass in the form of such things as asteroids to do cheaply.</li>
<li>There is something special about <i>Raddus</i> which makes it uniquely qualified to ram. If this is the case, <i>Raddus</i> is just a machine, and any machine can be duplicated.</li>
<li>The only theory which is not immediately dismissable is that there is something special about <i>Supremacy</i>, perhaps related to the hyperspace tracker. Admiral Holdo could not have successfully rammed any other ship. Even so, Holdo would have had to know how the hyperspace tracker worked, at least well enough to know that it created this vulnerability. Besides, one of the <i>Incredible Cross Sections</i> books contains text to the effect that hyperspace tracking was merely an algorithm, and the hyperspace tracker on <i>Supremacy</i> was merely a large computer facility.</li>
</ul>
The point is that in any conceivable scenario, whatever Admiral Holdo did at that moment could have been, and therefore should have been, weaponized long before. Either it was, and all battles in the galaxy would have been different, or it wasn't, because it is impossible. kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-30983762764087455622019-04-06T21:58:00.000-06:002019-04-06T23:20:29.077-06:00C++ ClevernessYukari was in some ways an amazing piece of code. It didn't actually drive the robot all that well, but of all the things in it, I am most proud of it's self-documentation. Each run of Yukari produced a file which recorded three things:<br />
<br />
<br />
<ol>
<li>Data packets showing what the robot was doing and what it was thinking</li>
<li>An image of its code and any other files I thought needed attaching</li>
<li><i>A description of how to parse the record file</i>, partly in English, partly machine readable. With this description, anyone who had the file could in principle write a piece of code to parse the file.</li>
</ol>
<div>
I am redoing this code for the Loginator. While it is easy to just use the same logic that I used on Yukari, that code was inefficient. It did the following:</div>
<div>
<br /></div>
<div>
<ul>
<li>The packet start function took a pointer to a string describing the packet, and each kind of fill function took a pointer to a string describing the field. This was nice because the documentation for each field in the code is right next to the actual code for it.</li>
<li>The start function and each fill function called the writeDoc() function, which took care of documentaion. After that was finished, it wrote the field.</li>
<li>The writeDoc() function kept track of apids which have already been documented. If this apid has already been documented, writeDoc() returns immediately. Otherwise it write a field description packet.</li>
<li>In order to make this work, the actual packet data had to be stashed somewhere. If the packet was in the process of being documented, writeDoc() for a packet start set up pointers such that the packet being written went to this stash buffer, and writeDoc() got to create actual packets in the proper buffer.</li>
</ul>
<div>
What I have in mind is much more clever. It will see the compiler generate the core of the documentation packets at compile-time. These will then just be in ROM, which we have bucketloads of. I think this will involve template meta-programming and constexpr functions. </div>
</div>
<div>
<br /></div>
<div>
Each call to start and fill will be immediately followed by a template class instantiation. This class template will take as parameters the apid, the string description, and perhaps some other stuff (units, conversion, etc). The class will declare a couple of static constant member fields, which will result in them getting stuck in the .rodata section, or maybe a special section. Once it is someplace in the read-only image, the startup code will write out all the documentation in the same way that </div>
<div>
<br /></div>
<div>
The interface will look like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">start(apid_blah,TTC(0)); template class packet_doc<apid_blah,0,"This packet records exactly how blah things are">;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">fillu16(blah); template class packet_doc<apid_blah,t_u16,"This field records the blah level">;</span></div>
<div>
<br /></div>
<div>
It's not quite as clean as the old way, but it is purely a run-time thing. To begin with, we would have a template something like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">template<int A, int T, const char* D></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">class packet_doc {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> static const int __attribute__ ((section(".packet_doc"))) apid=A;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> static const int __attribute__ ((section(".packet_doc"))) fieldType=T;</span></div>
<div>
</div>
<br />
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> static const char* __attribute__ ((section(".packet_doc"))) desc=D;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">}</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">We could make things fancier by keeping track of the position in the packet using more advanced template metaprogramming.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;"><b>Update:</b></span><br />
<span style="font-family: inherit;">Nope, defeated. While you can use an address as a template parameter, a string literal doesn't necessarily have an address on its own. You can set up a const array with a string literal in it, and use that as the template parameter, but that starts getting way too ugly. I'll do it the old way, and use the bottom of the stack for temporary space. It isn't secure, because what happens if the stack and this buffer collide, but I'm not going to worry about that.</span></div>
<div>
<br /></div>
<div>
<br /></div>
kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-26812243197067165482018-10-17T15:33:00.000-06:002018-10-17T15:43:25.140-06:00Interesting things about Voyager 1 and 2 launches<blockquote class="tr_bq">
There is something fascinating about science. One gets such wholesale returns
of conjecture out of such a trifling investment of fact.<br />
<div style="text-align: right;">
<a href="http://www.twainquotes.com/Mississippi.html" target="_blank">--Mark Twain</a> </div>
</blockquote>
It's amazing how much we can do with such a small amount of data. The following analysis is based solely on the dates of launch and arrival of the Voyager spacecrafts, and an ephemeris of the planets.<br />
<br />
Voyager 2 was <a href="http://en.wikipedia.org/wiki/Voyager_2" rel="nofollow" target="_blank">launched</a> on 1977-08-20T14:29:00Z, while Voyager 1 was launched <a href="http://en.wikipedia.org/wiki/Voyager_1" target="_blank">later</a> on 1977-09-05T12:56:00Z. Voyager 1 passed Voyager 2 on the way out (the exact time depends on how you define "pass") and <a href="https://voyager.jpl.nasa.gov/mission/science/hyperbolic-orbital-elements/" target="_blank">arrived</a> at Jupiter on 1979-03-05T12:05:26TDB, while Voyager 2 arrived on 1979-07-09T22:29:51TDB.<br />
<br />
Notice that while the arrival reference is a full-blown set of orbital elements, we did not use these. Instead, we use the Lambert/Gauss targeting method to plot a course which departs from the center of the Earth on the indicated time, considers only the gravity of the Sun and ignores the gravity of the Earth, and arrives at the center of Jupiter at the indicated time. There is one unique trajectory which crosses the indicated places at the indicated times and is prograde.<br />
<br />
So, even though Voyager 1 is launched later, it arrives first. This is for two reasons:<br />
<ol>
<li>Voyager 1 has a higher heliocentric energy</li>
<li>Voyager 1 is launched on the "outside track". Normally the inside track is faster, but not in this case. If both spacecraft are going to intercept Jupiter, the first to arrive must be on the outside track because Jupiter is moving from "outside" to "inside".</li>
</ol>
We end up with the following picture:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiszidtcucovCVbVUPiyL7JPElkJxQ-zWJxXIWDaUS1rs4Kt-ATMUr8Jf6xK2VEa1Yn_CaV8MKMre3UfUErwG8kv69Y6mfcymFpjdh1C104QxmsXC9SofFGlwje-luUXrXIqPDwUtZjpUY/s1600/Voyager+Earth-Jupiter+Trajectory.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="850" data-original-width="1573" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiszidtcucovCVbVUPiyL7JPElkJxQ-zWJxXIWDaUS1rs4Kt-ATMUr8Jf6xK2VEa1Yn_CaV8MKMre3UfUErwG8kv69Y6mfcymFpjdh1C104QxmsXC9SofFGlwje-luUXrXIqPDwUtZjpUY/s640/Voyager+Earth-Jupiter+Trajectory.png" width="640" /></a></div>
<br />
There is a lot to look at in this picture, so here are the thousand words. Red is Voyager 1, blue is Voyager 2. The small white circle is the orbit of Earth, with the sun marked as a yellow dot, Jupiter the next larger white circle, and Saturn the largest. The tick marks are 30-day intervals starting at the launch date of Voyager 2. This means that the ticks for Voyager 1 and 2 are directly comparable. We see Voyager 1 pass at about the 4th tick mark. A zoom in reveals that from this perspective directly from Ecliptic North, the trajectories actually cross twice. Normally the trajectory diagrams don't show them crossing at all.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://www.nasaspaceflight.com/wp-content/uploads/2011/09/A10.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="289" data-original-width="351" height="263" src="https://www.nasaspaceflight.com/wp-content/uploads/2011/09/A10.jpg" width="320" /></a></div>
Another interesting thing is that the orbit of Voyager 2 if it did not intercept Jupiter, would have continued about 1AU past Jupiter's orbit. However, Voyager 1's orbit would have gotten almost out to Saturn. Did they use a larger launch vehicle for Voyager 1? No, both spacecraft had almost identical launch speeds (vinf=10.951km/s for Voyager 1, 10.316km/s for Voyager 2). The main difference is just the direction of launch. Voyager 2 was launched above the ecliptic plane (departure asymtote 17 deg above the ecliptic) while Voyager 1 was launched almost in the ecliptic (departure asymtote 4 deg above ecliptic). Also, Voyager 1 was launched closer to the direction of travel of the Earth. Voyager 2 was launched 18 degrees inward, while Voyager 1 was launched 4 deg outward.kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-26156880174144705332018-08-10T16:42:00.001-06:002018-08-10T16:42:18.680-06:00Another blast from the pastOnce upon a time, I participated in the Internet Ray-tracing Competition. It turns out that they have an archive, and it turns out that most of the source code for my images are still there. I was able to recover another one today:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvUtIsJIeVZ7BkeWMj68Nhj7cTcdsevk4kMNSq_7TPuNRMH0Sjs99jSLhcO-_y9-4StIHc81qah-S_0Vur_dUu1pQdEHQT7QDbzyS3YWLH3I0sBccUMEyY09dGKcF5jKe1F8iSDJna5BY/s1600/cdjmath.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1080" data-original-width="1440" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvUtIsJIeVZ7BkeWMj68Nhj7cTcdsevk4kMNSq_7TPuNRMH0Sjs99jSLhcO-_y9-4StIHc81qah-S_0Vur_dUu1pQdEHQT7QDbzyS3YWLH3I0sBccUMEyY09dGKcF5jKe1F8iSDJna5BY/s640/cdjmath.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A modern render of an ancient image</td></tr>
</tbody></table>
My notes say that this took 6 hours to model and 1h30m45s to render on an AMD K6-2 300MHz 192MiB memory machine. The re-render at the same resolution takes 4.423 seconds on an Intel Xeon E3-1505M laptop with 32GiB memory. This is over 1200x faster. At HD resolution it takes about 13.7 seconds.kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-10160452308182932742018-02-06T10:31:00.000-07:002018-02-06T11:26:25.191-07:00Falcon HeavyToday is the scheduled launch date for the Falcon Heavy demonstration. As of this writing, the launch is on schedule for an 11:30 MST launch.<br />
<br />
There is very little official guidance from SpaceX as to what to expect. Elon Musk has stated that minimum mission success is clearing pad 39A far enough such that any further failure doesn't destroy that pad. There has never been a catastrophic failure at pad 39A, and Musk would like to keep it that way.<br />
<br />
However, the plan is to do a boost, then three burns of the upper stage. The first finishes launch to LEO. The second is about 30 seconds, and seems to put the booster into a GTO-like orbit. Lifting a heavier spacecraft into full GTO takes about a minute, so there is some hint that this will go into an elliptical orbit that is short of GTO. Part of the demonstration is a 6-hour coast. They are doing it on this flight because the upper stage is very similar to any normal Falcon 9 upper stage, and any demonstration on this stage would apply there. This coast is what would be needed for a 3-burn GSO direct insertion, that apparently is very interesting to the military. For one thing, it would demonstrate that the upper stage could put a GPS satellite directly into its target orbit, like the much more expensive Delta IV medium. A bit more oomph and a similar endurance would put a spacecraft directly into GSO.<br />
<br />
In any case, the consensus on NasaSpaceflight is that the target high orbit is one with a period of around 6 hours. After this coast, the second stage would be back at perigee, ready to take maximum advantage of the Oberth effect.<br />
<br />
SpaceX has claimed that they will put the payload (A cherry-red Tesla Roadster) into an "earth-mars heliocentric orbit". The launch window for Mars is in May, so they will be launching 3 months out of the window, but since this is such a light payload, they should have plenty of C3 and probably could target Mars if they wanted. However, I think that they will instead target an orbit with periapse at Earth and a C3 typical of launching to Mars. The payload will reach the vicinity of Mars orbit, but Mars will be far far away by then. In fact, to be responsible about Planetary Protection, they should launch into an orbit which will not actually intersect Mars orbit at all, so that there is never any possibility of the car impacting Mars.<br />
<br />
Running the numbers based on the Trajectory Planner 1.1.1 from Orbit Hangar, I get a departure C3 of 23.9 km^2/s^2, with a departure today and an arrival on October 17, 2018. The flight time is 252 days. This C3 is high for a Mars launch, but should be doable with such a light payload.<br />
<br />
If they are targeting Mars, then the launch vehicle must be able to adjust azimuth in order to target Mars at any time during the window. If they are just going for a given C3, they can use the same azimuth whenever they go. Since ASDS is parked somewhere definite to catch the core stage, I estimate that they are targeting a fixed azimuth independent of launch time.<br />
<br />
There are no signs of high-gain antennas or solar panels on the payload, so it is almost certain that once the battery runs down, the payload will become inert. However, the payload is an electric car, with many many amp-hours of battery life. The car radio might run for hours or days.kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-66707255527824007122017-08-21T09:00:00.002-06:002017-08-29T11:02:10.910-06:00Live, from the eclipse path<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioxgMFIQxUWd9na_SFTlD5SgNw0W4J1AK1IoSYqx6ZXM7OeNPNOy_-4baoZtTJ3sPKzWZoMj0EG11Qli_khANsiWTyreOhSfLNmaQndIZay_tZTdj5foWK3_T1WkBlux14q-V4LgnDAlA/s1600/20170821_102645.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="900" data-original-width="1600" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioxgMFIQxUWd9na_SFTlD5SgNw0W4J1AK1IoSYqx6ZXM7OeNPNOy_-4baoZtTJ3sPKzWZoMj0EG11Qli_khANsiWTyreOhSfLNmaQndIZay_tZTdj5foWK3_T1WkBlux14q-V4LgnDAlA/s640/20170821_102645.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The Kid Attractor - works on adults too.</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyDwhpexB57LXVXOMZjRUOFSGC0CABB6egI2iajxzCf1t-KmzX2F9LN5gPrVEqiurKCcGeFOEg-0ua779-h-pfat6YuE6vaznpHv9uHh48oCcBzABCdXH9dcd1eel6GqtEKFF-A9lYDNw/s1600/20170821_102743.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1600" data-original-width="900" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyDwhpexB57LXVXOMZjRUOFSGC0CABB6egI2iajxzCf1t-KmzX2F9LN5gPrVEqiurKCcGeFOEg-0ua779-h-pfat6YuE6vaznpHv9uHh48oCcBzABCdXH9dcd1eel6GqtEKFF-A9lYDNw/s320/20170821_102743.jpg" width="180" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Cookie Monster</td></tr>
</tbody></table>
I'm in Rexburg, ID where the crowds are not as bad as I feared. I left Logan, UT this morning at 4:00am. Traffic on I-15 was interesting. It never slowed down below the speed limit, but there were easily 20 times as many cars heading north as opposed to south. We arrived at Rexburg at 7:30 and found plenty of parking in a church parking lot just south of the Temple.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggUgc6Sw7fy4m2CIz9dg2VIaQL7cKTORqTsT-_WQXEVK_8NjjYzhvEZlcwlHx_qWfYnyEEIehTLlcm8ttaiFzyijDQiSFRsmgI2W5m7mEksTkREro1L6KZ-tcumifowodjeZhKbV5p1F4/s1600/2017-08-21+09.01.36.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="900" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggUgc6Sw7fy4m2CIz9dg2VIaQL7cKTORqTsT-_WQXEVK_8NjjYzhvEZlcwlHx_qWfYnyEEIehTLlcm8ttaiFzyijDQiSFRsmgI2W5m7mEksTkREro1L6KZ-tcumifowodjeZhKbV5p1F4/s320/2017-08-21+09.01.36.jpg" width="180" /></a></div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDKmfNk1gAZNKBfn_eh0FXGlt48pChiHJbMIuFwMzl8aBTXb3X-4GiXAXQ2beFMDtMYcRISEKN8sfVsODfK23nkxGdAjKL3Pf2Ah8JCSkZ1Q8Wq5T9_yTRA06taDky_N3NBCaai4vSfwU/s1600/20170821_102214.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1600" data-original-width="900" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDKmfNk1gAZNKBfn_eh0FXGlt48pChiHJbMIuFwMzl8aBTXb3X-4GiXAXQ2beFMDtMYcRISEKN8sfVsODfK23nkxGdAjKL3Pf2Ah8JCSkZ1Q8Wq5T9_yTRA06taDky_N3NBCaai4vSfwU/s320/20170821_102214.jpg" width="180" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Just after first contact</td></tr>
</tbody></table>
Things of note:<br />
<br />
<br />
<ul>
<li>It did get noticeably darker as the eclipse passed 50%</li>
<li>We could see the shadow approach from the west. It was hazy that direction, and I could see a band of dark start at the horizon. It looked like it was getting vertically wider rather than closer.</li>
<li>Totality itself cannot be done justice in pictures. I took pictures but used my eyes too. Light level was close to that of just after sunset. The sky was a deep blue purple. We saw the diamond ring, which was MUCH brighter than the rest of the corona. The corona is white, and rather narrow with 3 long streamers (more than one solar diameter) One at 12:00, one at 1:30 and one at 7:00. The disk of the moon was the same color as the sky.</li>
<li>I thought I got crowd shots, and everyone around said that it was totally worth it. Unfortunately for some reason either the video was never captured or was deleted :(</li>
<li>No one bothered to stay long after totality - we just all packed up and ignored the reverse of the spectacle we had just seen.</li>
</ul>
<br />
<br />
See you all in 2024!kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-59346152130922067972017-07-22T12:11:00.000-06:002017-07-22T18:23:42.826-06:00Today's Episode of "It's my responsibility, but...""...The datasheet diagrams didn't have the pins labeled."<br />
<br />
This time, it is the encoder board.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggpuQAym9PMR1mpJLprp5CcutsXNax1o7LOT9hFQktBt3sYnmsqA1bNbjvtIAKr7me1dr8I143EzzMlE6QJOeD6Kg6BDO6eT9bS1fE_xvc1u0PmyJ66spc12b8EGbuJrcpJWHeUeKqggA/s1600/Screenshot+from+2017-07-22+11-55-54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="262" data-original-width="388" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggpuQAym9PMR1mpJLprp5CcutsXNax1o7LOT9hFQktBt3sYnmsqA1bNbjvtIAKr7me1dr8I143EzzMlE6QJOeD6Kg6BDO6eT9bS1fE_xvc1u0PmyJ66spc12b8EGbuJrcpJWHeUeKqggA/s1600/Screenshot+from+2017-07-22+11-55-54.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvC4vBTO-Me2blOw09X0Lsj1XN5IQyVW9mu84q1IncHJ0AQaWCnQJrr7AR3uzZMYBR9KVetQHrK0OTv0EYsQQVcnhIMtodQbZ_yt2epDzWMGrqNiEW6S6cr1NRcpU-1oNVp7HvpIQVuxk/s1600/Screenshot+from+2017-07-22+11-58-27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="219" data-original-width="274" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvC4vBTO-Me2blOw09X0Lsj1XN5IQyVW9mu84q1IncHJ0AQaWCnQJrr7AR3uzZMYBR9KVetQHrK0OTv0EYsQQVcnhIMtodQbZ_yt2epDzWMGrqNiEW6S6cr1NRcpU-1oNVp7HvpIQVuxk/s1600/Screenshot+from+2017-07-22+11-58-27.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN5_pEUsJGvuIsEySZ3q8uVpWl2Moa0mH6pdA58Fdkel08lMKIWl1CNtaWWNKwOc8m8F-CbNWsHZKG426fHjuoEX91X2downLGPxy2gYxHeP7ULaDLZHh1F-2cr9PuoCz51Z_jpqHSvCE/s1600/Screenshot+from+2017-07-22+11-59-40.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="212" data-original-width="282" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN5_pEUsJGvuIsEySZ3q8uVpWl2Moa0mH6pdA58Fdkel08lMKIWl1CNtaWWNKwOc8m8F-CbNWsHZKG426fHjuoEX91X2downLGPxy2gYxHeP7ULaDLZHh1F-2cr9PuoCz51Z_jpqHSvCE/s1600/Screenshot+from+2017-07-22+11-59-40.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Here, it isn't obvious which pad in the footprint goes with which pad on the part.<br />
<br />
It doesn't matter anyway, because a closer examination of the footprint and the Digikey list of optical sensors reveals that <i>I bought the wrong part</i>. The footprint on the board is marked QRE1113, but it is actually for an Omron EE-SY193. D'oh!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKFKYJG9SO2xg_zxHzIdbx2gxRFVjPkKmLAehD_svwghm8mCmD6jP1vrGSiWJS4TyPogXtJtU-sLHrGCcnBRiToXJ5SMQQUSou_iwVh6nsXxVIZLbbNiYrb1lsA8LoGI1vuUN2Ftk_19k/s1600/Screenshot+from+2017-07-22+12-06-08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="947" data-original-width="665" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKFKYJG9SO2xg_zxHzIdbx2gxRFVjPkKmLAehD_svwghm8mCmD6jP1vrGSiWJS4TyPogXtJtU-sLHrGCcnBRiToXJ5SMQQUSou_iwVh6nsXxVIZLbbNiYrb1lsA8LoGI1vuUN2Ftk_19k/s640/Screenshot+from+2017-07-22+12-06-08.png" width="449" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHEszJoTLCHDxnqbRvP2YEgdonGY721AJl8HIKSG5wEb3N_ufJ_zkecFyfTLkfC0V42O9RG0w-RwJWjMgWZsLtvRX0q1FUjErser8smBDUBP0mTpbQ4GtOikoKNoxA64IEzjgNbgNW7PY/s1600/Screenshot+from+2017-07-22+12-08-29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="734" data-original-width="1254" height="374" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHEszJoTLCHDxnqbRvP2YEgdonGY721AJl8HIKSG5wEb3N_ufJ_zkecFyfTLkfC0V42O9RG0w-RwJWjMgWZsLtvRX0q1FUjErser8smBDUBP0mTpbQ4GtOikoKNoxA64IEzjgNbgNW7PY/s640/Screenshot+from+2017-07-22+12-08-29.png" width="640" /></a></div>
<br />
Well, back to the fab again, for both boards. I'll use the QRE1113 parts that I have to test if the parts even fit in the hole. If not, I'll have to use the EE-SY193.<br />
<br />
All parts of this project are my responsibility. It can't be otherwise, since there isn't anyone I report to or who reports to me. My partner is a special case.<br />
<br />kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-29505071266297650832017-07-21T15:32:00.001-06:002017-07-21T16:34:43.592-06:00Dagnabbit!That beautiful purple board I received yesterday won't work. If it had been stuffed and plugged in, it would have immediately shorted out anything plugged into it.<br />
<br />
One of the features on the board is a super-wide (for a 6-mil PCB) strap between the two adjacent 5V pins coming from the Pi. When I had a close look at it, I saw a thin little gold crescent around part of the hole. It took a little bit for it to dawn on me that this was the ground plane, which was visible through the mask, plated in gold, and not protected from shorting with the other contacts:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEthSHFeizxMOBQRhazZHm8HC2NcISnZ2atHGZhoQar65UNCbos-zXD2cwlb7tV3kBP1mv48E0WXfjUuAHMTcKPLYBEewjXLP0SxWRkGDL3pJ8th5PM_F8UPGCvWYcopAs_Kl5OajnMRw/s1600/DagnabbitCloseup.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="241" data-original-width="424" height="362" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEthSHFeizxMOBQRhazZHm8HC2NcISnZ2atHGZhoQar65UNCbos-zXD2cwlb7tV3kBP1mv48E0WXfjUuAHMTcKPLYBEewjXLP0SxWRkGDL3pJ8th5PM_F8UPGCvWYcopAs_Kl5OajnMRw/s640/DagnabbitCloseup.png" width="640" /></a></div>
The effect is everywhere on the board. I saw it first on the strap in the upper-left, but this magnified version shows it on the motor power section and connection to the Arduino. Note the crescents around the top of D3~ and RST, and the slivers of ground plane visible through the mask around the isolator footprint.<br />
<br />
I don't think that there is anything that can be done to fix it. I also don't think that the encoder board is affected, so at least I can still use that. I'll try to stuff it, but I will check continuity closely. If any of the solder bridges the 6mil gap, then the short will exist.<br />
<br />
I'm pretty sure the problem comes from settings in the ground plane. Since OSHPark can make 6mil boards, I take full advantage of the feature. Unfortunately, this interacts with a bad default in Kicad. Fortunately, that is easy to change, but it would have been nice to know fifteen dollars and two week ago. The money isn't a big deal, it's the time.<br />
<br />
Kicad was even trying to tell me that something bad was about to happen. Not from the DRC (although that would have been nice) but in the images. Here is the old, bad design:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUn4ctkvgJ7Oa4CrNY5mlaK79o8FBbGCu_WUVeB_jxr4yHvAGv0uRSEX66EP1VV6ED3V-bqzSBjksvtc7LJDBKWVITR03r8e9TeMiJ7SYktI47UyYtHEkI2KW0rdA3cje_wPauwmKkBUA/s1600/Screenshot+from+2017-07-21+16-12-51.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="424" data-original-width="583" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUn4ctkvgJ7Oa4CrNY5mlaK79o8FBbGCu_WUVeB_jxr4yHvAGv0uRSEX66EP1VV6ED3V-bqzSBjksvtc7LJDBKWVITR03r8e9TeMiJ7SYktI47UyYtHEkI2KW0rdA3cje_wPauwmKkBUA/s1600/Screenshot+from+2017-07-21+16-12-51.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The purple rings around the pads are the soldermask gap. You can see a slight red tinge around the edge where the mask gap overlaps the ground plane.</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR8lI1TrpZMkkx2W08cDIs6LPZMLnAqzdf9pvck6oI75oxw5Ffx4AToLZWlfpxPyBs8wi9jGvNfsJhvIwGb2jdl_y6C3YcqA1-bvcm0iy1Mf8QphtKJq83WKnGP2ri_DhICg91WHh6TS4/s1600/Screenshot+from+2017-07-21+16-16-04.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="919" data-original-width="1221" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR8lI1TrpZMkkx2W08cDIs6LPZMLnAqzdf9pvck6oI75oxw5Ffx4AToLZWlfpxPyBs8wi9jGvNfsJhvIwGb2jdl_y6C3YcqA1-bvcm0iy1Mf8QphtKJq83WKnGP2ri_DhICg91WHh6TS4/s640/Screenshot+from+2017-07-21+16-16-04.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The OpenGL preview shows it too, perhaps in an easier-to-understand form.</td></tr>
</tbody></table>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL9t030pKWv4rq2kxLZpSYjdb3sDfJyhaC-Lhgoc6Hi57m-LM22H6f7kX0IlvgKEgrKv2AfHR1RqxJFqCIVjT40PHKL99Xo95hYrMu8wIWD_lsupqgdtOMd7HgdwoZVEciuhss8tShr8M/s1600/PadMaskClearanceMenu.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="213" data-original-width="532" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL9t030pKWv4rq2kxLZpSYjdb3sDfJyhaC-Lhgoc6Hi57m-LM22H6f7kX0IlvgKEgrKv2AfHR1RqxJFqCIVjT40PHKL99Xo95hYrMu8wIWD_lsupqgdtOMd7HgdwoZVEciuhss8tShr8M/s1600/PadMaskClearanceMenu.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">To fix it, use the Dimensions/Pads Mask Clearance menu option</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1Atk-ucxxzyRv3LynVHV-3FU-DZdC98BAHKYIos-JHdkXnpgmFlI75_8OgBPwV51R9iv4iZpE6OZw-Z6LJfeXOXjrum1OtxQLfrVH3sQ28Drvhdq8aibWqfwX7JvovOVaG6yuax3G2go/s1600/Screenshot+from+2017-07-21+16-14-05.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="323" data-original-width="357" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1Atk-ucxxzyRv3LynVHV-3FU-DZdC98BAHKYIos-JHdkXnpgmFlI75_8OgBPwV51R9iv4iZpE6OZw-Z6LJfeXOXjrum1OtxQLfrVH3sQ28Drvhdq8aibWqfwX7JvovOVaG6yuax3G2go/s1600/Screenshot+from+2017-07-21+16-14-05.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Change the Solder mask clearance value from its default... (Yes, I design my boards in US units. You got a problem with that?)</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilJJ-ecg9hjdBoZZ36hamylnPUaqB_HSqEl9lYHy0qEfGUmPjomGuMtvglzVMklONcTjs4YXW2xrd6T87UUYxJk_8oFGG8IKBNvYcNBsmblIBUFs-cPNk6PE0tNQr2SrtR6zP0cQqIM1k/s1600/Screenshot+from+2017-07-21+16-14-31.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="322" data-original-width="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilJJ-ecg9hjdBoZZ36hamylnPUaqB_HSqEl9lYHy0qEfGUmPjomGuMtvglzVMklONcTjs4YXW2xrd6T87UUYxJk_8oFGG8IKBNvYcNBsmblIBUFs-cPNk6PE0tNQr2SrtR6zP0cQqIM1k/s1600/Screenshot+from+2017-07-21+16-14-31.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Change it to zero.</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4iWg2pTE2x9xpF15tgfJjExioEA1OGP7EctUP0FWnZb4hQXHBrMaHz2XVNgwGKE0j34EWLrswm01NwCNmtEddYoxqtOxpjTCYofaufMfjARrdVMsdUI41ZayAE65dQptwWmcLJ2Ka36k/s1600/Screenshot+from+2017-07-21+16-28-14.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="785" data-original-width="697" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4iWg2pTE2x9xpF15tgfJjExioEA1OGP7EctUP0FWnZb4hQXHBrMaHz2XVNgwGKE0j34EWLrswm01NwCNmtEddYoxqtOxpjTCYofaufMfjARrdVMsdUI41ZayAE65dQptwWmcLJ2Ka36k/s640/Screenshot+from+2017-07-21+16-28-14.png" width="568" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The borders of all the pads are now black, indicating the solder mask gap doesn't span the space between the pad and the ground plane</td></tr>
</tbody></table>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj04uCRjtEdxNnxe1cp3DmDyvUivvdjxZc2czqVdDx_4DpHGPSzgU4bisWnwF3M9ji28yiABT-TT7iXgnrWNlAbNI9Dg7VMi8HxyV9w3M_mV72cpDRJKVm6XSps4OarjH-3LWg6RBRqFk4/s1600/Screenshot+from+2017-07-21+16-29-01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="909" data-original-width="876" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj04uCRjtEdxNnxe1cp3DmDyvUivvdjxZc2czqVdDx_4DpHGPSzgU4bisWnwF3M9ji28yiABT-TT7iXgnrWNlAbNI9Dg7VMi8HxyV9w3M_mV72cpDRJKVm6XSps4OarjH-3LWg6RBRqFk4/s640/Screenshot+from+2017-07-21+16-29-01.png" width="616" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
This option has settings in several places: The global setting I talk about above, the zone setting (which I haven't found), the footprint setting, and the individual pad setting. The later settings in the list have priority over the earlier ones, but the earlier ones are used as defaults.<br />
<br />
In general with modern fab processes, you should set this value to zero. This will describe a hole in the solder mask the exact size of the pad. The fab can and will edit this to match their process, so don't worry about getting it too small.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe width="320" height="266" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/b_woPkyQ8cs/0.jpg" src="https://www.youtube.com/embed/b_woPkyQ8cs?feature=player_embedded" frameborder="0" allowfullscreen></iframe></div>
Thanks to a YouTube video by My 2uF for help finding this setting.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
<br />kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-91741973367454388552017-07-20T18:46:00.001-06:002017-07-21T15:24:13.942-06:00Yukari 5 parts!<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTM7wMKNVcVmmFMy1QqLAguzkSyStKdJecDAK4SIwquEPg3M8bW-bme1tvTcfWugZuJkXTUbKRdLpRg92cnPZSuV3SjTm_h_pUMSN-3MZSefE5SPjBps-NY5rXoEnRCCZoNfeuedRbGAI/s1600/20170720_175514.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1600" data-original-width="1322" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTM7wMKNVcVmmFMy1QqLAguzkSyStKdJecDAK4SIwquEPg3M8bW-bme1tvTcfWugZuJkXTUbKRdLpRg92cnPZSuV3SjTm_h_pUMSN-3MZSefE5SPjBps-NY5rXoEnRCCZoNfeuedRbGAI/s640/20170720_175514.jpg" width="528" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Front side of each board</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix7f4CHnRj3uvvSuJOXVxalBlGPvRNjkYFoKYeDzQhGUnBtmMEDrsJgMZ8OCWZpPE0Q-pxg802j4M93POkku1LJjCJh-8hCb1PDTEV3arm6ne-PqOXsittCycNMHIhRsE6yUtsS2vsdFw/s1600/20170720_184137.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1321" data-original-width="1600" height="528" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEix7f4CHnRj3uvvSuJOXVxalBlGPvRNjkYFoKYeDzQhGUnBtmMEDrsJgMZ8OCWZpPE0Q-pxg802j4M93POkku1LJjCJh-8hCb1PDTEV3arm6ne-PqOXsittCycNMHIhRsE6yUtsS2vsdFw/s640/20170720_184137.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Look how awesome the gold logos look!</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNztqsnAq47cRedTOcf2COqUXPqgm2x_S1V9CR5B6uk7RPFsL3BqMpH_ej-Jg91YOBEzBGMlcpaRmo-i-NQhVM2HaAA73q47V-jK5nzquXVUUXowVL8t94pF3RaaPdNJOthvFAEiKLCG8/s1600/20170720_175930.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="900" data-original-width="1600" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNztqsnAq47cRedTOcf2COqUXPqgm2x_S1V9CR5B6uk7RPFsL3BqMpH_ej-Jg91YOBEzBGMlcpaRmo-i-NQhVM2HaAA73q47V-jK5nzquXVUUXowVL8t94pF3RaaPdNJOthvFAEiKLCG8/s640/20170720_175930.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Here is the Pi wearing it's hat (upside down, because the logos are so cool)</td></tr>
</tbody></table>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1fVYvuk3Vk3AaK25d1iMKd4eLgd6gwwyp2PJZZJ72kqBv92YjabnhfZAvTAJxQMkNgssNH0A0bemFdbDUtbUo-PEp3QO7lyv__idjIDOmTDFTRh8KBYcJ-Z_e-9szrSDs6UF82ADsdR4/s1600/20170720_181248.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="900" data-original-width="1600" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1fVYvuk3Vk3AaK25d1iMKd4eLgd6gwwyp2PJZZJ72kqBv92YjabnhfZAvTAJxQMkNgssNH0A0bemFdbDUtbUo-PEp3QO7lyv__idjIDOmTDFTRh8KBYcJ-Z_e-9szrSDs6UF82ADsdR4/s640/20170720_181248.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">And the hat wearing it's accessories</td></tr>
</tbody></table>
<br />
<br />
<br /><br />kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0tag:blogger.com,1999:blog-966886899841220442.post-20956149391915605172016-12-30T12:47:00.002-07:002016-12-30T12:47:30.253-07:00ContinuitySpoilers for Rogue One below the fold<br />
<br />
<a name='more'></a><br />
<blockquote class="tr_bq">
<div class="quote">
It is a period of civil war. Rebel spaceships, striking from a hidden base,
have won their first victory against the evil Galactic Empire. During
the battle, Rebel spies managed to steal secret plans to the Empire's
ultimate weapon, the DEATH STAR, an armored space station with enough
power to destroy an entire planet.
</div>
</blockquote>
<br />
<br />
<dd><blockquote>
―Episode IV opening crawl</blockquote>
</dd>
Rogue One ends with the Battle of Scarif. During this battle, many things are going on at once, as is typical for a Star Wars battle.<br />
<br />
<ul>
<li>On the surface, team Rogue One steals the tape holding the plans from the archive tower, then climbs the tower to the high-gain antenna on top. No high-bandwidth signals can get through the shield, and the plans are sufficiently large that a high-bandwidth signal is needed.</li>
<li>Simultaneously their ship is patched into the base comm system. A low-bandwidth (voice) signal is sent up telling the fleet to be ready for a large transmission, and to take the shield down in order to get it. The fleet knows what Rogue One is looking for in general, because they talked about it in council before Rogue One went rogue.</li>
<li>The fleet takes down the shield by destroying the shield gate. Apparently the shield was generated from there.</li>
<li>Once the shield is down and the antennas are aligned, the data is transmitted to the rebel fleet flagship <i>Profundity</i> and copied onto a physical storage medium ("tape").</li>
<li>The Death Star arrives but ignores the fleet. They know that Vader is arriving soon to deal with the fleet, and they don't know who to point the big gun at. For all they know, there might be a copy of the plans in every ship in the fleet. In fact, only <i>Profundity</i> has a copy.</li>
<li>Vader arrives in <i>Devastator</i> exactly as the fleet is attempting to jump to hyperspace and escape. Several ships, including <i>Profundity</i>, are intercepted.</li>
<li>The tape is hand-carried to the docking bay of <i>Profundity</i> where it is slipped through the airlock door of <i>Tantive IV</i> just as it is launching. Vader personally tried but failed to stop this hand-off. <i>Tantive IV</i> escapes into hyperspace.</li>
</ul>
So at this point, Vader knows that several ships escaped before he got there, but probably from interrogating the crew of <i>Profundity</i>, they knew that it was on that blockade runner that escaped, and probably the registration of it, so they knew they were looking for <i>Tantive IV</i>, but might not have known who was on it.<br />
<br />
An unknown amount of time later, <i>Devastator</i> intercepts <i>Tantive IV</i> over Tatooine. (It is possible that <i>Tantive IV</i> took an indirect route from Scarif to Tatooine. I don't know if it matters.) <i>Tantive IV</i> is looking for General Kenobi, and somehow <i>Devastator</i> tracked them there. Presumably <i>Tantive IV</i> arrived first and was following normal protocols for landing in the wilderness when <i>Devastator</i> showed up. Kenobi didn't know at that time that anyone was coming.<br />
<br />
<pre><b> INT. REBEL BLOCKADE RUNNER - CORRIDOR
</b>
The evil Darth Vader stands amid the broken and twisted bodies
of his foes. He grabs a wounded Rebel Officer by the neck as
an Imperial Officer rushes up to the Dark Lord.
<b> IMPERIAL OFFICER
</b> The Death Star plans are not in the
main computer.
Vader squeezes the neck of the Rebel Officer, who struggles
in vain.
<b> VADER
</b> Where are those transmissions you
intercepted?
Vader lifts the Rebel off his feet by his throat.
<b> VADER
</b> What have you done with those plans?
<b> REBEL OFFICER
</b> We intercepted no transmissions.
Aaah... This is a consular ship.
Were on a diplomatic mission.
<b> VADER
</b> If this is a consular ship... were
is the Ambassador?
The Rebel refuses to speak but eventually cries out as the
Dark Lord begins to squeeze the officer's throat, creating a
gruesome snapping and choking, until the soldier goes limp.
Vader tosses the dead soldier against the wall and turns to
his troops.
<b> VADER
</b> Commander, tear this ship apart until
you've found those plans and bring
me the Ambassador. I want her alive!
The stormtroopers scurry into the subhallways.</pre>
<br />
The "transmission" <i>was</i> a transmission (from Rogue One to <i>Profundity</i>) but is a physical tape at this point. Vader knows that <i>Tantive IV</i> was at Scarif and was the ship that escaped with the tapes, but no one on <i>Tantive IV</i> knows that the star destroyer that intercepted <i>Profundity</i> was Vader's ship, so the captain tried to bluff his way out.<br />
<br />
kwan3217http://www.blogger.com/profile/15778227959469064835noreply@blogger.com0