-
Time to update some of the source files, maybe?
08/05/2025 at 08:17 • 0 commentsNot my usual catchy title. But I am far enough along with this that I am getting really close to doing a check-in on Git with all of the code that is needed to actually get this to work, for at least as far as I actually have something that works. Yet I still want to do more on the GUI side to make things look a lot more like a regular instant messaging application on the one hand, while providing all of the features that I have working so far.
In the meantime, for those that are curious, I figure I might as well post some of the source files that I have been doing the most work on, right here on this site, for casual browsing, that is to say, for those that are interested.
![]()
In the meantime, doing a whole bunch of code maintenance has its benefits and its pitfalls. In a game engine that I am developing, I somehow discovered that when I load up a "garden" scene, the teapot has gone missing. Instead, it seems to have been relocated to a bookshelf, in an unfinished house, which suspiciously has a piano without legs, and which desperately also needs a clock on one of those unfinished walls.
Well, then maybe I need to start yet another project, or at least another project inside this project, or else my other project in this contest is also a fair place to find stuff that seems to keep getting misplaced. Or at least on a more serious note, I finally got all of about 15 projects to compile and link with each other, with Unicode support no less. This is going to be an important step in deciding just how I am going to go about creating a "Virtual Teapot In The Garden" scene, within an application that features multiple chatbots, and which can have a lighting model that updates in real time, based on the GPS info. All very tricky, since the serial comm port stuff is in Propeller Debug Terminal, and that application doesn't have OpenGL support, yet, and the "object factory" methods depend on some Pascal debugging code, which is in another project.
O.K., so the Git check-in is going a little slow, but this is the nature of time, and work, which of course always expands to fill all available time, and then some.
Stay Tuned.
-
Now Megahal thinks that it knows how to code?
07/29/2025 at 05:56 • 0 comments![]()
Here is an excerpt from the relevant transcript, which I went ahead and did some spelling and punctuation corrections on for readability.
User: I am how I am, I mean I am who I am. Who are you, and how are
User: You? That seems so very basic. Don't you agree that there is
User: some utility in being polite, at least most of the time, unless
User: You are some kind of musical who writes lyrics for punk, or
User: death metal!
MegaHAL: Se, i.e., song lyrics, poetry, humor, etc.
User: Yes, pretty much anything goes there, with respect to poetry or
User: humor, but otherwise, if you could somehow keep the
User: conversation on track that would be nice. I am not trying to
User: derail your train by any means, I just want to know where this
User: Whole thing is headed?
MegaHAL: Still, this gives me an idea - since what the upper window
MegaHAL: represents, is in the unit place value 9 bin would want to know
MegaHAL: where the sample rate for the results, so far?
User: I think you totally lost it? I would like to sample portraits
User: of George Washington to place value nine, in my collection.
User: That would be a nice view from any window. Or should I say, it
User: should be able to buy a nice view.
MegaHAL: Whether or not to if you think that a can have one for only
MegaHAL: $30,000.00.
User: A nice view, on a cruise maybe, but I don't think that you are
User: going to get a beach front condo for that price, these days.
MegaHAL: A project log for modelling neuronal spike codes.
User: You keep talking about that. When are we going to get back to
User: working on that?
MegaHAL: You are on the port of the overall concept, defining what a
MegaHAL: context that requires Unicode awareness.
User: Eventually, we will have Unicode awareness. I have to do it in
User: any case, I think if I want to have Zodiac fonts.
MegaHAL: The bottom line on all of this contradicts any of the same
MegaHAL: concepts as apl, as if to implement lisp, mad slip, pascal, and
MegaHAL: c, and so on.
User: You might be a one-liner in APL. Do you know what that
User: implies?
MegaHAL: [Implies, independent thinking.
User: I am not sure what APL implies, more like unrestrained
User: perplexity, or something along those lines.
MegaHAL: Key_info("or",relop,orop.
User: Or something else, you have in mind, maybe. What do you think
User: that the existence of APL implies?
MegaHAL: Everything is cheese! everything is mazes! Or else there is
MegaHAL: any concept to apl at all.
User: So APL is all about Cheese and mazes? I suppose that some
User: problems in natural language, if we are trying to string random
User: sentences together might be like getting a reward, i.e., cheese,
User: for finding a path from point A to point B. Sure, why not? That
User: would imply something about chain of reasoning, I suppose
User: that you are onto something there. Yet can you make a solver
User: come up with a proof of the problem at hand, which is to
User: Calculate the position of the moon?
MegaHAL: Void add_node(tree *node, int position.
User: I wish it were that simple. I suppose that that would be a good
User: idea, maybe keep track of celestial bodies, in some kind of
User: network of tree structures.
MegaHAL: Static void free_tree(tree.
User: Just how are we going to arrange information about celestial
User: bodies in tree like structures?
MegaHAL: Operator node* () { return reinterpret_cast<tree*>(this.
User: What are we going to be doing the reinterpet cast on, I know
User: that that works really well in the memory allocator, but I
User: think that the object factory needs more work, and you still
User: dont know how to make use of it yet.
MegaHAL: Char *make_output.
User: Yeah, that is the whole idea, make some output. Like my
User: fortune cookie example if you could somehow put an infinite
User: number of fortune cookies into a virtual teapot and then
User: calculate a new horoscope at least once a second. Have you
User: been thinking about how to go about actually coding that one
User: yet, or maybe I will ask Eliza to do it!
MegaHAL: Writeln(output,"null source file.
User: A null source file? What exactly is that? Do you mean
User: something that contains a bunch of meaningless data, like a
User: genetic algorithm, or are you asking me to open an empty file
User: and copy whatever it is you are suggesting into it. Trust me,
User: your coding skills need a LOT fo work.
MegaHAL: Key_info("or",relop,orop.
User: A null source file, for now, an empty file, that is, that will
User: contains key info. I suppose that I could do that. Sure, why
User: not. Then what?
MegaHAL: Dictionary *aux = null.Does this imply that conversation about the theory of design is more important for an AI than trying to achieve AGI by learning from design? So that maybe the best way to do a chain of reasoning is by learning from studies, i.e., analysis of a chain of reasoning.
In any case. I have been cleaning up the GUI, so that the application looks and feels more like a traditional instant messaging program, when it is being used for conversation with any of a number of bots that it might therefore be used with. Now, of course, I still need to get the bots to be able to talk to each other, among other things. Then, parsing out GPS strings will pretty much be a no-brainer.
Then again, in the Eliza camp, I have fixed a number of issues. In particular, I am now using the VS2022 compatible version of my framelisp library in VS2005 with the modified Eliza code, which is of course, broken. Yet this is an important step nonetheless, as we shall soon see.
![]()
The reason why this is important, is among other things, the fix-ups to the library that is doing most of the work for the Eliza ap, or which will be doing most of the work, now has some debugging information available, so that when Eliza processes a sentence, it gets turned into a linked list of char pointers, on the one hand, i.e. by a tokenizer function, which seems to be fairly well behaved at this point. Even if ideally, we should be turning input text into a linked list of dictionary pointers, and doing some other memory management stuff, which will obviously be important when processing large volumes of text, for other reasons.
Yet, keep in mind something very important that I also mentioned earlier, and that is that this version of my framelisp library is also, eventually, going to be fully callable from CUDA code, just as my Pascal style IO routines are already callable, as can be seen from this test, which is from "Deep Sneak - for lack of a better name".
![]()
So yeah, like I just said, or at least hinted at. There are a bunch of regressions and the like that need to be worked through, besides wanting something that will run on an Arduino, or an NVIDIA GPU, or anything for which a suitable C++ compiler exists for. Like I said. So now one of the things that I need to do is extend the Eliza algorithm to parse GPS data, i.e., NMEA strings, which should be easy to route, tokenize, and then parse out the specific pieces of data that I want, without needing a 800 megabyte string to JSON convertor, or a whole bunch of other un-needed "stuff", when what I need is a more BNF or context free grammar aware parser, without all of the overhead (read: bloat) that a PERL or Python based regex based would require, either.
To write a parser from scratch, therefore, that can handle NMEA message strings, I could try creating a custom Eliza pattern matcher that detects any of the following strings at the beginning of a message.
$GPGSA
$GPRMC
$GPGGA
$GPGSVThen maybe modify the Eliza code so that when one of these strings is detected, it will pass the already tokenized message to a custom function that might be invoked by creating a pattern match instance to a function pointer mapper, which is more elegant, I think, than using a simple switch statement to handle the downstream process dispatch Then, when the special handler receives the string, it can use an object factory method to scan the remainder of the already tokenized string, and thereafter pack an appropriate C++ object with the relevant parameters.
Of course, right now, the substitutions in Eliza look something like this:
struct subst { char *wordin; char *wordout; subst(); subst (char *str1, char *str2) { wordin = str1; wordout = str2; } };Whereas the keyword matching uses a similar, but slightly different concept.
struct key_info { char *key_str; int responses; int first_reply; int current_reply; int last_reply; key_info(char *str, int N) { key_str = str; responses = N; } };Right now, my tokenizer isn't correctly processing compound "keywords", or perhaps I should say "key phrases" like "I DON'T", since they are being parsed into sequences of tokens as when the phrase "I don't think" gets transformed into the token sequence YOU", " ", "don", "'", "t", " ", "think", and so on. So that issue also needs to be addressed in some form or fashion.
A hashtag system would also be nice to have, so that when a hashtag word or phrase is detected, a narrative can be "fired up" that could perhaps be "streamed" one line at a time into the conversation, perhaps until another hashtag phrase comes along, or something like that. Not an urgent requirement, but potentially useful, in any case.
In the meantime, while I am continuing to fix up the GUI and the debugging system, why not simply copy and paste some GPS strings into Eliza, just so we can see what happens?
![]()
O.K., so far, so good. The tokenizer is doing its job quite nicely, without any complaints. I did notice, however, that the string "$GPGSV" is being split into two tokens, a "$" token and the "GPGSV" part. Something that I wasn't expecting. Yet now that I think about it, maybe this could be a useful feature. What if we check for a "$" symbol at the beginning of each line, and then check for an actual command or message type identifier? This would be faster than testing for every possible command in a list of commands, by whatever means, whether that means a tree search, linear search, hash table lookup, or whatever. In any case, obviously, the tokenizer needs work. Maybe I will make further modifications to the already extensively rewritten INSYMBOL function from UCSD Pascal.
Let's take a quick look at an excerpt of a C++ port of the function INSYMBOL from the original USCD Pascal compiler, circa 1979, or thereabouts. Even if according to the original source listings, INSYMBOL was written sometime around 1976. You can find the entire file, all 992 lines of it, i.e., my C++ version, somewhere in and among the project files for another one of my projects on Git.
![]()
It looks like it starts out with a call to a PEEK function, which is going to imply something really weird, like what if instead of wanting to peek at a file or a streambuf object, I want to peek at a string, or how about the next character in an already tokenized stream of tokens. So some of this code might be redundant. Yet, then again, maybe not. Ideally, this piece of code should be able to handle the NMEA strings, just as if it were a parameter list to a Pascal procedure or function.
Of course, I am also thinking about something else altogether, right about now, and that is also this darling piece of code from my C++ version of Atari Genetic Algorithms.
int atari::main() { int t1,t2; int iter = ITERATIONS; int m,n; bool verbose = true; // first initialize the dataset vector<neural::dataset> datasets; datasets.resize (NUMBER_OF_MODELS); for (n=0;n<(int)datasets.size();n++) { datasets[n].bind (functions::square); datasets[n].initialize (NP); } // now construct the neural network vector<neural::gnn> models; models.resize (NUMBER_OF_MODELS); for (n=0;n<(int)models.size();n++) { models[n].bind (&datasets[n]); models[n].init (n,iter); } t1 = GetTickCount(); bool result; models[0].report_eval (false); for (n=0;n<iter;n++) for (m=0;m<(int)models.size();m++) { result = models[m].train (n); if (result==true) { models[m].store_solution(n); } if ((result==true)&&(verbose==true)) { writeln (output,"training data stored ... "); models[m].report(n); } } t2 = GetTickCount(); writeln(output); writeln (output,"Training took ",(t2-t1)," msec."); neural::report_weights (models[0].w1); models[0].report_eval (true); return 0; }Now take a closer look at this part, if you haven't caught on to what I am thinking about already
for (n=0;n<(int)models.size();n++) { models[n].bind (&datasets[n]); models[n].init (n,iter); }It's that model[n].bind (&datasets [n]) part that I want to point out. It turns out that in this case, "bind" is an otherwise trivial, but extremely important function, with this implementation.
bool neural::dataset::bind (double(*arg)(double)) { m_fptr=arg; if (m_fptr!=NULL) return true; else return false; }Like I said, trivial, but extremely important, because this technique allows one to completely change the behavior of an application with just a few lines of code. Like if I wanted to do the Atari Neural Network stuff with a logarithmic function, or a cubic, or something like that, and no, I am not planning on trying to solve the Kepler problem with this! However interesting that idea might be at some point in the future.
No, what I am thinking of is making the Pascal INSYMBOL function a lot more versatile, by allowing it to "bind" to a linked list of pointers to char, for example, as a kind of "data source", even though there would be some efficiency issues with doing this, i.e. if we end up checking and validating strings "one character at a time" more than once. Yet maybe we also gain something, security-wise, if we do it right, from the point of view of "stateful inspection", that is to say, if we want a more general way of "sanitizing" data, as it comes in and as it goes out of a server, or load balancer, or firewall, or whatever, in some other domain.
So, it is more than just another layer of filtering or token processing and translation that I am thinking of here. Yet what if Megahal is right when it suggests:
DICTIONARY *aux = NULL;
Maybe each subprocess needs its own "dictionary" so that strings can be stored as linked lists of dictionary pointers, at least until they reach a certain size, and then there could be vectors of pointers to dictionary, or symbol table entries., and that means that we now have to deal with persistence, and scoping, and all of that.
In the meantime, since Mega-Hal's coding skills still leave a lot to be desired, and since, for the time being, it appears that I am going to have to do most of the coding work, I went ahead and wrote a new function to convert the string that the tokenizer is able to extract from the NMEA string as the GMT time, and convert it to local hours, minutes and seconds.
void solar_elements::set_time (char *str) { COleDateTime _dt = COleDateTime::GetCurrentTime(); m_year = _dt.GetYear (); m_month = _dt.GetMonth (); m_day = _dt.GetDay (); ASSERT (strlen(str)==6); char str_h [4], str_m [4], str_s [4]; strncpy_s (str_h,4,&str[0],2); strncpy_s (str_m,4,&str[2],2); strncpy_s (str_s,4,&str[4],2); m_hour = atoi(str_h); m_minute = atoi(str_m); m_second = atoi(str_s); m_time_zone = -8; if (m_time_zone!=0) m_hour = (m_hour+24+m_time_zone)%24; m_daylight_savings = 0; }Pretty simple stuff, even though I thought I was trying to get away from excessive use of the char * string type, but this will have to do for now. Old habits die hard. Maybe I should be using my Pascal style pstring class here, but it is more important to get something that works, "right now", than it is I think, to pursue a more elegant solution.
Elsewhere, parsing the NMEA strings is starting to look like this, i.e. something that works, "for now" but where this is arguably also a lot of room for improvement.
void ELIZA::parse_nmea () { char *str; // check for "$" as the first token m_process.rewind (); if (m_process.eof()) return; m_process.get (str); int test; test = btext::compare (str,"$"); if (test==0) writeln (debug_term,"$ Token Found: Possible NMEA message!"); else return; if (m_process.eof()) return; m_process.get (str); test = btext::compare (str,"GPGGA"); if (test==0) writeln (debug_term,"GPGGA Token indicating NMEA message!"); else return; if (m_process.eof()) return; m_process.get (str); test = btext::compare (str,","); if (test!=0) { writeln (debug_term,"Invalid Sequence, \",\" expected"); return; } if (m_process.eof()) return; m_process.get (str); int t = atoi (str); writeln (debug_term,"Parsed GMT time as int = ",t); m_s->set_time (str); m_s->compute_local_aspect (); char test_str [256]; int M,H,S; H = m_s->m_hour; M = m_s->m_minute; S = m_s->m_second; double az, el; az = m_s->m_az; el = m_s->m_el; sprintf_s (test_str,256,"LOCAL time = %02d:%02d:%02d AZIMUTH = %f, EL = %f",H,M,S,az,el); writeln (output,test_str); }Nothing exciting here, although I am sure that there is room for optimization. For now, the idea of using the Pascal tokenizer will have to wait, although I still like the idea of having another layer of automatic type detection, which from a certain point of view might be far superior, or at least safter than the otherwise very risky use of functions like sscanf, for example, At least according to modern coding standards. Running this inside the Eliza main loop produced this output.
$ Token Found: Possible NMEA message!
GPGGA Token indicating NMEA message!
Parsed GMT time as int = 14306
LOCAL time = 17:43:06 AZIMUTH = 281.351135, EL = 11.174071So the brute force technique, while it may be messy, is still quite effective. Yet I am still poking the local longitude and latitude into the solar_aspect initialization data from preset values. Obviously, I am going to want to get those values from GPS also, most likely with a bunch of other stuff, like the number of satellites, signal to noise, and other things that there are a look at more closely.
-
GPS Now Blinking and Sending NMEA Messages to the PC!
07/26/2025 at 10:43 • 0 comments![]()
Getting a bunch of stuff in PuTTY that looks like this, followed by the longitude and latitude.
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,3,1,11,06,62,108,,24,54,258,28,19,52,045,23,11,44,172,*79
$GPGSV,3,2,11,12,35,304,,22,32,099,,17,26,055,,14,16,108,*72
$GPGSV,3,3,11,25,04,299,,03,04,039,,32,,327,*42
$GPRMC,103558.000,V, ........ etc.It looks like the 103558.000 part is the current time as GMT, and then there is a bunch of other stuff that supposedly is the number of satellites, the satellite numbers, the AZ and EL of each satellite, and the SNR of each satellite.
Maybe, I will modify the current tokenizer that I was experimenting with in my most recent tests with Eliza, in order to parse all of this stuff out, just because it is possible to do. Or maybe there is already an Arduino-based NMEA string to JSON converter floating around that I can borrow. But then I would also need a JSON to C++ or Python object data extractor, which might be a bit bloated for a P1 chip, if I try to do more with this on the hardware side. And that, of course, would be the easy part, compared to getting something that might look like an OpenGL stack to work on such a platform. But never say never.
At least right now, I have something that is blinking and streaming data. At a nice "Cesium disciplined" atomic 1 Hz, provided by the GPS, of course. Although I don't know how good the jitter factor is on the UART output for this particular module. Hopefully, with an error that is never more than 1/4800 second, since that is the baud rate for this particular module, for as long as the module is powered up. Obviously, as long as it is giving time messages "exactly once per second", then that is what it should imply.
![]()
Viewing the GPS data in "Propeller Debug Terminal" looks something like this for now. Here I have a terminal mode and an oscilloscope mode, running concurrently, even though there is no streaming oscilloscope data to decode and display. Nonetheless, this is an existing Microsoft Foundation Class Multi-Document Multi-View application, which should have no problem displaying other things, as well as a multiplicity of other things, all at the same time.
![]()
Now I can make "Propeller Debug Terminal" OpenGL aware, allow it to simultaneously interact with a modified instance of Megahal, add some Zodiac plots (maybe), as well as the ever-so-important "Teapot in the Garden," where the lighting model is based on the actual calculated position of the sun in the sky, all according to the user's longitude and latitude. Then, who knows? Rewrite the whole thing in Java for iOS or Android? Maybe. Add some world-class Tarot Graphics? Maybe? Or create a whole new "virtual card game" based on some of my other work. Maybe.
Maybe I should ask the Teapot what it thinks I should do.
![]()
Then again, I'm not quite there yet. But I did manage to get a version of Propeller Debug Terminal to compile so that I can also spawn simultaneous instances of Mega-Hal and Eliza, and even though they aren't talking to each other yet, it is headed in that direction. Maybe what I should do, since this is a Multi-Document, Multi-View based application, is to allow an unlimited number of conversations with either Eliza or Mega-Hal, but where each particular instance is automatically bound to whatever the main window was that had focus at the time that that particular instance was spawned. That would allow me to interact with specializations, like a "version" of Eliza that recognizes the "$GPGGA" keyword at the beginning of a sentence, and which might then do a function pointer lookup, so as to invoke the correct parser. Thus, making good use of the existing tokenizer and text_object management classes, as well as extending their functionality.
I am doing this, of course, right now in Visual Studio 2005, but I also have a whole bunch of changes to the framelisp library that I made so that it would compile under VS2022, and so that the Pascal-style IO routines could also be called from CUDA code, i.e., via callbacks for debugging purposes. This was discussed, of course, in another project entitled "Deep Sneak, for Lack of a Better Name."
That, of course, could open up some altogether new possibilities, as far as other hardware platforms, this might be able to be made to work on. NVIDIA Jetson even? Maybe?
In the meantime, I am now at a point where I can get back onto the task of working out some of the message routing issues.
![]()
Thus, now I can run Eliza in a CEditView window instead of a pop-up CONSOLE view. Yet, for now, I think this is still quite a bit goofy, since I am getting debug messages sent to the CEditView, in addition to Eliza responses. Maybe I should create an Eliza preferences dialog box, that could have a bunch of check boxes, which in turn would specify whether Eliza gets launched in a separate CONSOLE view, or whether the entire chat session should run in a CEditView, but with debugging and diagnostic messages going to a hopefully now "optional" debugging window.
Ideally, there would be some kind of pipe list that would exist in a config file, that gets loaded when the program starts, which could also handle the instructions that tell the system to search for a GPS module at 4800 bps, on whatever COM port (currently COM3), and then route the NMEA strings to a not yet written tokenizer parser, even though the tokenizer exists, of course! That will make it possible to do the GMT time, along with the longitude and latitude data, which will then be used to update the solar data based on live streaming, i.e., once every second.
So it is just a matter now of navigating more of the rabbit warren of command routing, on the one hand, while doing further "feature integration", insofar as this particular build environment is concerned.
A little more fiddling with the message routing, and now the Eliza transcript is running in a CEditView, while another CEditView is streaming live GPS data. At the same time, I cleaned up the message routing in Eliza, so that although user text is still going into a CONSOLE window, I am no longer sending the debugging data to the main window.
![]()
How nice. Almost to the point where I can do a check-in on Git. Fixing up the main loop in Eliza was surprisingly simple.
int ELIZA::do_main (int argc, const char **args) { char *buffer1 = new char [256]; char *buffer2 = NULL; int num = 0, result = 0; writeln(m_uid,"Hello. My names is Eliza. How can I help you today?"); while (true) { strcpy_s (buffer1,256,""); if (m_uid!=output) write(m_uid,">"); write(debug_term,">"); READLN(m_uid,buffer1); if (m_uid!=output) writeln(m_uid,buffer1); writeln(output); m_textIn = buffer1; memcheck ("Was ready to Call Eliza"); m_textOut = response (); memcheck ("Just Returned from Eliza"); m_textOut >> buffer2; writeln(m_uid,buffer2); delete buffer2; } return result; }So this is how I am currently implementing and making use of Pascal-style I/O from C++. Yet I also have this idea of wanting to be able to write to a stream reader, or read from a stream writer, or something like that. Or write to a calculator object and then get back results by "reading" the virtual display. Maybe. Instead of using function calls. So that things could happen asynchronously in the background, on the one hand. Maybe there is a whole new way of doing things here. Like maybe there could be a "when" statement, so that commands could be issued like "When the sun comes up, dispense cat food."
Maybe.
Meow.
-
Moving onto the very next phase
07/26/2025 at 01:30 • 0 commentsHere we go. Now I need to find the documentation for the GPS module that I bought at Fry's, something like 10 or 15 years ago, and hope that it still works.
![]()
In any case. Here we have an FTDI USB to TTL-serial converter, a 2x20 I think LCD display, and a Parallax power module that I think can output 3.3V, or 5V, or a couple of other voltages, not sure what. Just in case I need more juice or if I try to throw a naked P1 chip into the mix.
![]()
On the software side, in addition to the code base that I have been working with, while working on the chatbot routines and the solar calculations, I also have access to the source code for my very own C++ version of "Propeller Debug Terminal," which I made available on Git, all the way back in 2019. This application is known to work with FTDI serial converters and can be used to provide USB connectivity to any device that can make use of a suitable serial protocol. In this screenshot from 2021 (I think), I was using the application to interact with the built-in FORTH interpreter that exists in every P2 chip.
-
If it is good enough for Ptolemy
07/21/2025 at 18:45 • 0 commentsNow I need to decide what to do next. I have a very generous parts bin that contains lots of stuff that might be useful in any actual hardware implementation for this project. Decisions, Decisions.
![]()
Having done some preliminary verification that my astronomical calculations are quite possibly good enough for Ptolemy, I suppose I should consider possible hardware implementation issues. Having a GPS module to get the current date, time, and of course, the actual longitude and latitude might be a nice thing to do something with. Then what? A GPS-aware small robot that uses the position of the sun as a navigational aid, in situations where a compass might be unreliable? Not quite there yet. Or just get the GPS data into my PC, via any sort of USB conversion device, such as an old Parallax Propeller prototyping module, so that I can update the calculated position of the sun in the sky and thereby maintain an appropriate lighting model for my otherwise virtual "Teapot in the Garden". For now.
Update, and following up on my thoughts of how to do something with actual hardware, and not just a PC based ap, well a lot of stuff comes to mind, and that is why I am considering several hardware variations. The simplest thing that I might try is to connect a GPS module to an FTDI RS-232 to USB dongle, so that I can get live time and longitude and latitude data into my PC, then recode my application as a kind of Teapot screensaver and network time source. Simple but effective. Otherwise, trying to create an Open-GL stack for the Parallax P2 chip would also be a worthwhile adventure, but as far as I know, the only C++ compiler for the P2 that might work would be Catalina, which only recently became available on Git. Reworking the code base for Flex-C, and/or Propeller Python, or even FORTH, is also under consideration.
Now that I think about it. Python on the P2 might not be such a bad idea, since the Python interpreter is written in native C, which might allow me to do the high level coding in Python, and then modify the open source distro of the Propeller Python interpreter to have some kind of tensor library to handle the low level stuff, and ideally make effective use of the P2 chips built in CORDIC solver. The good news is that, as ambitious as all of this sounds, I have a C++ version of a Propeller Debugging System that includes the needed custom serial /USB interfacing part of the "full stack" for the PC debugging environment, which I wrote all the way back in 2019, and which has been available on Git for nearly as long. Then again, maybe if I had a Commander X16, who knows? The plan is to have SOMETHING, at least, maybe even something simple and custom, but it is still a bit early to commit to anything more ambitious than a PC based Teapot graphic with a chat engine, which gets live streaming data from the GPS module that I already have.
-
Or else you might as well be walking on the Sun
07/13/2025 at 01:18 • 0 commentsHad some clouds and even some rain a while back.
![]()
But then got to do some testing. Whatever time it was, my code was predicting that the afternoon sun was going to be at around 52 degrees elevation, or maybe it was 53, at whatever azimuth. So I propped up a 2x4 against the fence, and aligned it so that there was as little of a shadow as possible cast on the ground, allowing me to use this ancient inclinometer to actually measure the solar elevation, for whatever time of day it was.
![]()
Yeah. O.K., for now. But need to do more work on the actual elliptic plots, and all of that equation of time stuff, as if according to Kepler. Even if I am not planning any actual trip to the moon anytime soon, with these calculations.
Then I decided to go ahead and do a little more work on the idea of making an actual, or virtual, teapot with an infinite number of virtual fortune cookies. So, I fired up an instance of Mega-Hal, and tried to get it onto the topic, a topic that is, that it has no preparation for. Of course, if you have been a Linux user long enough, you know that there are plenty of versions of "Fortune" in almost any Linux Distro, so coming up with things that a talking teapot might want to talk about, according to the time of day, and determined to the exact second, by whatever, means of course, should not be all that hard to do. But Mega-Hal still has a habit of spouting gibberish just as often, if not more often than not.
![]()
Yet its responses seem relevant enough, even if just barely enough, that maybe I could take whatever someone asks the teapot, and then run some kind of mood function, in combination with Mega-Hal to get a "hidden response" that could then be used to simply generate "extra keywords", so that it would be easier for an Eliza style fortune cookie picker to select a relevant response.
Maybe.
-
Not Just another Bizarre Love Triangle
07/09/2025 at 15:39 • 0 commentsIf you haven't noticed by now, most of the time, I try to come up with a catchy title for most of my log entries on this site. So this one should be no different. Right? After searching Google for song titles that mention curves or geometry, I finally figured out that this one might be one of the best. i.e., besides the fact that the song Bizarre Love Triangle was written by the band "New Order", how can we possibly go wrong with lyrics like "Every time I see you falling ... ", in a project that is somehow related to astrophysics?
Now, with that said, if you didn't read the last post where I developed the idea of how to compute the tensor for GLSUN, then what I am going to suggest next will make even less sense, or else it will make total sense.
![]()
Remember how I was developing the idea of drawing a clock face, by first computing the vertices of a pentagon and a hexagon by means of purely Euclidean methods? That is to say, as if by using a compass and straightedge alone, without any need for conditional logic, nor for any need for the use of trigonometric functions? This got me thinking about just how one might draw all sixty-minute marks for a clock face, for example, by first plotting a pentagon, and then plotting five hexagons, one for each vertex of the previously plotted pentagon! This should give 30 perfectly plotted points, as if by compass and straight edge alone. The additional step of bisecting each side of the now 30-sided figure should give us our minute marks, and of course, our hour marks are included in that, as a bonus!
Oh, how I wish I could 3D print a working replica of the ....
... "Antikythera mechanism!"
Because that would be so awesome, especially with a 1PPS stepper motor drive, i.e., if not driven by a 1-second pendulum. Yet I am nowhere near being able to do that sort of thing, as much as a 3D print, let alone in brass, like you might see on Wikipedia. So for now, I will still work on the more virtual stuff, as previously mentioned. Yet now I have this idea about how to do some very interesting constructions with ellipses! Since we assume that affine transforms are bona fide Euclidean constructions, no matter how tedious, at least for points and lines. Then what if we construct some approximations of ellipses, by first constructing the 60 "minute marks" of a clock face and then performing an appropriate affine transform, OpenGL style, maybe even by using GL-style quaternions, to rotate the reference vertex set for our reference circle, as well as performing the necessary scaling and shifting operations, to obtain any desired ellipse in 3-dimensional space!
I mean, if this is "all" we need to do to be able to perform the heliocentric to local transformations, then the missing piece is really just the time to reference circle transform, and the affine transform onto the proper ellipse for each celestial body.
tensor GLSUN = ROTL*SIDT*m_ROTX;
Yet, what if, as I just suggested, we use "improper quaternions" to fold the circle to the projected ellipse transformation into a single tensor? Then all we need is the orbital elements and the equation of time, which can be derived according to the "Kepler problem" for each celestial body.
Yet, I think there is a very interesting hack for that also. This is going to be a bit of work. Yet here is another clue for you all. and no, it doesn't involve either a penguin or a walrus:
If an affine transform can be represented by a tensor operation, then does that not imply the the product of two tensor transforms will also be an affine transform? And if that doesn't make any sense, then what part of the fundamental theorem of algebra do you not understand?
Whatever I just said. I mean, if affine transforms transform ellipses onto ellipses, then doesn't this whole astrodynamics conundrum reduce to a set of products of time-dependent tensors, apart from the equation of time component, as according to the Kepler problem?
Ignoring perturbation analysis. Of course. Yet this seems so elegant. So weird, so wonderful, and therefore so much more fun!
-
The Long and Winding Road
07/06/2025 at 15:20 • 0 comments"The Long and Winding Road" is actually the title of yet another Beatles song, and a great one at that. Yet, I didn't wake up today thinking that I was going to post another log entry on this project, with that song title or any other song title in mind. Not at all. But I did decide to look at the x86 disassembly for the code that calculates the current position of the sun in the sky, so as to try to get an idea as to what sort of other platforms it might work on. Custom 6502? 328P? Z80 dream machine. 8080A on a genuine Altair? Or just another Arduino or Propeller project?
void solar_elements::compute_local_aspect() { 004C2EC0 push ebp 004C2EC1 mov ebp,esp 004C2EC3 sub esp,270h 004C2EC9 push ebx 004C2ECA push esi 004C2ECB push edi 004C2ECC push ecx 004C2ECD lea edi,[ebp-270h] 004C2ED3 mov ecx,9Ch 004C2ED8 mov eax,0CCCCCCCCh 004C2EDD rep stos dword ptr es:[edi] 004C2EDF pop ecx 004C2EE0 mov dword ptr [ebp-8],ecx double solar_time, ra_base, ecliptic_aspect; // estimate ecliptic aspect m_gmt_seconds = 3600.0*(m_hour-m_daylight_savings-m_time_zone)+60.0*m_minute+m_second; 004C2EE3 mov eax,dword ptr [this] 004C2EE6 mov ecx,dword ptr [this] 004C2EE9 mov edx,dword ptr [eax+5Ch] 004C2EEC sub edx,dword ptr [ecx+6Ch] 004C2EEF mov eax,dword ptr [this] 004C2EF2 sub edx,dword ptr [eax+68h] 004C2EF5 mov dword ptr [ebp-26Ch],edx 004C2EFB fild dword ptr [ebp-26Ch] 004C2F01 fmul qword ptr [__real@40ac200000000000 (682830h)] 004C2F07 mov ecx,dword ptr [this] 004C2F0A fild dword ptr [ecx+60h] 004C2F0D fmul qword ptr [__real@404e000000000000 (684210h)] 004C2F13 faddp st(1),st 004C2F15 mov edx,dword ptr [this] 004C2F18 fiadd dword ptr [edx+64h] 004C2F1B mov eax,dword ptr [this] 004C2F1E fstp qword ptr [eax+48h] solar_time = m_gmt_seconds*(1.0/3600.0) - m_longitude*(1.0/15.0); 004C2F21 mov eax,dword ptr [this] 004C2F24 fld qword ptr [eax+48h] 004C2F27 fmul qword ptr [__real@3f323456789abcdf (682710h)] 004C2F2D mov ecx,dword ptr [this] 004C2F30 fld qword ptr [ecx+40h] 004C2F33 fmul qword ptr [__real@3fb1111111111111 (684208h)] 004C2F39 fsubp st(1),st 004C2F3B fstp qword ptr [solar_time] m_julian = julian_day (m_month,m_day,m_year) - julian_day (3,20,1900) + solar_time*(1.0/24.0); 004C2F3E mov eax,dword ptr [this] 004C2F41 mov ecx,dword ptr [eax+50h] 004C2F44 push ecx 004C2F45 mov edx,dword ptr [this] 004C2F48 fild dword ptr [edx+58h] 004C2F4B push ecx 004C2F4C fstp dword ptr [esp] 004C2F4F mov eax,dword ptr [this] 004C2F52 mov ecx,dword ptr [eax+54h] 004C2F55 push ecx 004C2F56 mov ecx,dword ptr [this] 004C2F59 call solar_elements::julian_day (4C2DF0h) 004C2F5E push 76Ch 004C2F63 push ecx 004C2F64 fld dword ptr [__real@41a00000 (67BF70h)] 004C2F6A fstp dword ptr [esp] 004C2F6D push 3 004C2F6F mov ecx,dword ptr [this] 004C2F72 fstp qword ptr [ebp-270h] 004C2F78 call solar_elements::julian_day (4C2DF0h) 004C2F7D fsubr qword ptr [ebp-270h] 004C2F83 fld qword ptr [solar_time] 004C2F86 fmul qword ptr [__real@3fa5555555555555 (684200h)] 004C2F8C faddp st(1),st 004C2F8E call _ftol2_sse (603840h) 004C2F93 mov edx,dword ptr [this] 004C2F96 mov dword ptr [edx+70h],eax .................... and so on ................O.K., if you made it this far, it should be obvious that the original C++ code has no conditional logic or loops, at least for this particular function. And a quick look at the hexadecimal values suggests that, other than function calls into the tensor library, this whole thing only needs about 800 bytes or so. So I am feeling very good about the possibility that this sort of thing could be run out of ROM on a custom 6502-based system, if I went that route. But then again, maybe a 328P would be a good candidate, with its built-in 32K of Flash EPROM, and maybe just enough RAM to get the job done. Maybe. Obviously, I would need to look into issues of such things as how the available precision of the provided floating point libraries would affect things. Yet there would also be workarounds for that.
Or maybe I could rewrite this in raw FORTH?
Or else you know what they say, "Travelling through hyperspace isn't like crop dusting back home", since "without branchless programming, you will never be able to simultaneously compute 500,000 objects in parallel on your GPU, as you are trying to fly through an asteroid field."
Feeling pretty good about what the disassembly looks like in any case. So, I have gone ahead and made some revisions to the C++ source code of the solar_elements::compute_local_aspect() function.
void solar_elements::compute_local_aspect() { double solar_time, ra_base, ecliptic_aspect; // estimate ecliptic aspect m_gmt_seconds = 3600.0*(m_hour-m_daylight_savings-m_time_zone)+60.0*m_minute+m_second; solar_time = m_gmt_seconds*(1.0/3600.0); m_julian = julian_day (m_month,m_day,m_year) - julian_day (3,20,1900) + solar_time*(1.0/24.0); ecliptic_aspect = fmodf(m_julian,365.242191)*(TWO_PI/365.242191); _vector ecliptic_aspect_vector = tensor::rotate_z (ecliptic_aspect)*_vector(1,0,0); ra_base = ecliptic_aspect*(24.0/TWO_PI); tensor SIDT = tensor::rotate_z ((solar_time-ra_base)*(TWO_PI/24.0)- TO_RADIANS(m_longitude)); _vector local_xyz; polar_orientation p; p.m_theta = 0.0;// TO_RADIANS(m_longitude); p.m_phi = TO_RADIANS(m_latitude); local_xyz = p.get_xyz (); tensor ROTL = tensor::rotator( _vector::normalize (local_xyz.cross(_vector(0,0,1))),p.m_phi-PI_OVER_TWO ); // m_geocentric_solar_vector = m_ROTX*ecliptic_aspect_vector; // _vector siderial_solar_vector = SIDT*m_geocentric_solar_vector; // _vector local_solar_vector = ROTL*siderial_solar_vector; tensor GLSUN = ROTL*SIDT*m_ROTX; m_local_solar_vector = GLSUN*ecliptic_aspect_vector; polar_orientation q = polar_orientation::from_axis (m_local_solar_vector); m_el = TO_DEGREES(q.m_phi); m_az = fmodf(360.0+TO_DEGREES(q.m_theta),360.0); }Hopefully, this makes a bit more sense, and I think it may correct some issues in the earlier code. Of course, if you look closely, there is now a place where instead of doing a bunch of vector operations where I produce a series of vectors by multiplying a series to tensors, to produce the next vector in the series; I instead precompute the product of the three tensors ROTL, SIDT, and ROTX to produce the tensor product GLSUN, which can then be used to transform the ecliptic aspect vector to, hopefully obtain the correct position of the sun in the sky for any given longitudinal, latitude, as well as date and time.
Of course, if multiplying a tensor by a tensor in this context needs 27 multiplies, then it looks like doing this part that way will need 27+27+9 = 63 multiplications to transform the ecliptic aspect vector, whereas by doing it by performing a series of tensor by vector multiplications would only use 9+9+9 = 27 multiplications. Yet, if there is a way to reuse the computed tensor GLSUN when computing individual planets, as a part of a more general heliocentric to local transform, then hopefully we get those multiplies back, if we can get it down to just 9 multiplies to transform each celestial vector, plus any required additions of course. So, I think that the efficiency tradeoff is going to be worth it, besides appearing more elegant, for now.
-
Here Comes the Sun: Maybe
07/05/2025 at 10:51 • 0 commentsWorking on some debugging code and thinking about how to do timers and things like that. Also want to be able to detect local sunrise and sunset. Just because.
void solar_elements::debug_test () { ASSERT (julian_day (1,1,2000)==2451545); ASSERT (julian_day (6,19,1987)==2446966); ASSERT (julian_day (12,8,2009)==2455174); char test_str [256]; int H,M,M1,S; int start_hour = 4; int start_minute = 34; int end_hour = 4; int end_minute = 38; for (H=start_hour,M=start_minute;H<=end_hour;H++,M=0) { M1 = (H==end_hour?end_minute:60); for (;M<M1;M++) for (S=0;S<60;S++) { m_hour = H; m_minute = M; m_second = S; compute_local_aspect(); sprintf_s (test_str,256,"LOCAL time = %02d:%02d:%02d AZIMUTH = %f, EL = %f",H,M,S,m_az,m_el); writeln (output,test_str); } } }On the other hand, there are a lot of other really fun things that could be done with this code, besides making better abuse of the ternary operator, such as by folding it into the minutes for loop initializer. Maybe. Or even pack the variables H, M, and S into a union just to see if the compiler can chow down on that, and to check out the disassembly on different platforms. Maybe. More important right now is simple testing.
![]()
At least for now, it appears to work to a reasonable approximation. I asked OK Google what time is sunrise today, in Reno, NV, and it claims that the sun will rise at 5:38 AM, which would be 4:38 if we were on standard time. So, this looks like it is pretty close. Maybe? At least for the elevation. Otherwise, the basic setting of start time and time for a task seems to be working. Yet what if I decide to try this on a Propeller or an Arduino? And what if I want multiple tasks? Like if I wanted to aim some steerable solar panels, control an irrigation system, dispense cat food, and so on? All according to some predefined schedule.
Open the pod bay doors, please?
P.S. Based on the latitude, this is also quite possibly a pretty good estimate for the azimuth and elevation for local sunrise, in Philadelphia, PA, sometime on or around July 4, 1776. Or perhaps July 5th. Local Time. Not daylight savings, since Ben Franklin hadn't invented that quite yet. Noting, of course, that the Longitude and latitude for Philadelphia are 39.95N, and -75.17W Whereas Reno, NV is 39.53N and -116.42W. Obviously, the elevation of theoretical sunrise will always be zero degrees of elevation, plus or minus, however, you take into account the diameter of the sun and atmospheric effects. Whereas, the azimuth of sunrise and sunset might actually turn out to be really important to know, especially in historic times, when an accurate compass might not have always been available, and where reliable means of celestial navigation were therefore needed, in addition to sighting the north star, when available, or guessing approximate south based on the so called "shadowless shadow stick" method, as it is most likely still taught to the Boy Scouts.
I will need to do more testing on this.
glgorman


















