Stripcharting With Integral Tech BASIC GVG / 12 November 1986 A customer once asked us to provide a method of stripcharting with the Integral's HP-UX Technical BASIC; after a little research, I came up with a way of doing it and designed a demo program to show how it's done. The approach is simple; I plot graphics data on the display, use the Tech BASIC BREAD command to get lines of pixels off the display, and then dump them to the ThinkJet printer in printer-graphics mode. The BREAD command has the syntax: BREAD array_of_characters, byte_width BREAD reads a line of pixel data from the current pen position and to the right. The "byte_width" specifies the number of bytes of pixel data will read (this parameter must be an EVEN multiple of 2; that is, BREAD handles pixel data 16 bits at a time). The "array_of_characters" is a character array, DIMensioned to the "byte_width". ThinkJet printer graphics are controlled by the following ESCAPE sequences: ESC*r640S - Standard density graphics. ESC*r1280S - Double density graphics. ESC*rA - Begin graphics. ESC*bW - Send one line of n bytes. ESC*rB - End graphics. Standard printer graphics resolution is 640 dots per line - or 96 dots per inch in both the horizontal and vertical directions. Double density graphics doubles the horizontal resolution, but the vertical resolution stays the same; I stayed with standard printer graphics resolution . Note that these sequences can be merged into composite ESCAPE sequences when desired, since the first two characters that follow the ESCAPE character ("*r") are the same for each. Merging is accomplished by specifying the first sequence in full - but leaving the last character in lower-case, rather than upper case, as shown. A second sequence is added simply by appending its characters following the "*r" to the end of the first sequence. For example, to merge the the standard-graphics-resolution and begin-graphics sequences, I would write: ESC*r640sA The BREAD command and the printer escape sequences can be used to do a screen dump. First, the display must be SCALEd to match its pixel resolution - so the pen can be MOVEd to each row of display pixels for BREADing. Since the BASIC graphics window (with the menu keys turned off) has a resolution of 512 by 255 pixels, the proper SCALE command is: SCALE 0,511,254,0 Note that the vertical scale runs from "0" at the top of the display to 254 at the bottom; this makes it simpler to scan the display from the top down. Since each line of display pixels is 512 pixels pixels long, a 64-byte (512/8) character array must be DIMensioned to hold one line of display data; DIM line_buffer$[64] The printer can be put into standard display graphics mode by simple using the PRINT command and the appropriate ESCAPE sequence: PRINT USING "#,K" ; "~027*r640sA" The "#" image specifier disables a carriage-return/line-feed at the end of the string; the "K" image specifier eliminates unused blanks in a string (which crop up on occasion because of the way Tech BASIC stores string data). The "~027" in the string argument is simply the decimal code for an ESCAPE. Now dumping the display to the printer is easy. I MOVE to the beginning of each pixel row in the display - that is, perform: MOVE 0,0 - to - MOVE 0,254 - and on each row use BREAD to store the display line: BREAD line_buffer$, 64 I echo each line to the printer, prefacing it with the appropriate ESCAPE sequence (an "&" concatenates the two strings): PRINT USING "#,K" ; "~027*b64W" & line_buffer$ When all the lines have been dumped to the printer, I then turn off graphics: PRINT USING "#,K" ; "~027*rB" - and perform a final PRINT to clean out any remaining data in the printer's graphics data buffer. Of course, actually doing a stripchart is more complicated; several displays have to be spliced together to create a continuous output. The program on the next page provides some ideas on how to do that. 10 ! This program demonstrates generating stripcharts with IPC Tech BASIC. 20 ! 30 ! The approach is simple, and is based on using BREAD to read graphics 40 ! data from the screen and then dumping it to the ThinkJet while the 50 ! ThinkJet is in graphics mode. 60 ! 70 ! As a demonstration, this program generates a stripchart from random 80 ! data. The sample number is displayed at the left of the chart, and 90 ! the value (which ranges between -10 and +10) at the right of the 100 ! chart. The values are spaced at roughly 1 cm per value on the 110 ! chart. (The vertical graphics dot density is 96 dots per inch, which 120 ! comes to roughly 37.8 dots per cm; I use 38 as an approximation. 130 ! A more sophisticated algorithm could use uneven intervals to gain a 140 ! better approximation, but come on, folks, this is just a demo!) 150 ! 160 ! That yields seven samples per display; note that only part of 170 ! display is used, to make sure that consecutive display dumps splice 180 ! together evenly. (The program dumps a total of six displays.) 190 ! 200 ! Note that the display is scaled into pixel resolution; that gives 210 ! very precise control over the display. The pixel-resolution grid 220 ! is scaled off into regions as shown below: 230 ! 240 ! sample_number chart_area sample_value 250 ! | | | 260 ! v v v 270 ! 0 +------+------------------+-----------------+------+ 280 ! | | | | | 290 ! 12 + #1 | | | | 300 ! | | | | | 310 ! | | | | | 320 ! 50 + #2 | | | | 330 ! | | | | | 340 ! | | | | | 350 ! 88 + #3 | | | | 360 ! | | | | | 370 ! | | | | | 380 ! 126 + #4 | | | | 390 ! | | | | | 400 ! | | | | | 410 ! 164 + #5 | | | | 420 ! | | | | | 430 ! | | | | | 440 ! 202 + #6 | | | | 450 ! | | | | | 460 ! | | | | | 470 ! 240 + #7 | | | | 480 ! | | | | | 490 ! 254 +------+------------------+-----------------+------+ 500 ! ^ ^ ^ ^ ^ 510 ! |<-64->|<------192------->|<------192------>|<-64->| 520 ! 0 64 256 448 511 530 ! 540 ! Note that a line of display dots is stored in a character array 550 ! named "line_buffer"; a second array named "pad" stores "nulls" 560 ! to make sure that the output is centered on the printer. 570 ! 580 ! Note also that thr program makes special provisions on the first 590 ! and last screens to make sure that the first and last sample and 600 ! value labels are displayed completely. 610 ! 620 OPTION BASE 1 630 INTEGER ctr,sample,screen,first,last,pixel,last_pixel 640 INTEGER num_screens,position,last_position 650 num_screens=6 ! Set number of screens to dump. 660 REAL value 670 DIM line_buffer$[64] 680 DIM pad$[8] ! Padding to center graphics. 690 FOR ctr=1 TO 8 ! Fill pad$ up with "nulls". 700 pad$[ctr]="~000" 710 NEXT ctr 720 ! 730 PEN 1 @ GCLEAR ! Set white-on-black graphics. 740 SCALE 0,511,254,0 ! Scale to pixel resolution. 750 CSIZE 4 @ LORG 8 ! Define label style. 760 ! 770 DIM tbuf$[8] ! Seed random-number generator. 780 tbuf$=TIME$ 790 RANDOMIZE VAL(tbuf$[1,2]&tbuf$[4,5]&tbuf$[7,8]) 800 ! 810 DISP "~027&j@" ! Turn off menu. 820 PRINT USING "#,K" ; "~027*r640sA" ! Set 640 dots & graphics mode. 830 sample=0 @ value=0 @ last_pixel=255 ! Set up first sample. 840 ! 850 FOR screen=1 TO num_screens ! Dump screens: 860 ! 870 GCLEAR @ FRAME ! Set screen format. 880 MOVE 64,0 @ DRAW 64,254 890 MOVE 448,0 @ DRAW 448,254 900 MOVE 256,0 @ DRAW 256,254 910 ! 920 last_position=12 ! Create topmost entry on screen. 930 MOVE 64,last_position @ LABEL USING "3D,K" ; sample," " 940 MOVE 511,last_position @ LABEL USING "S2D.2D,K" ; value," " 950 ! 960 FOR position=50 TO 240 STEP 38 ! Create other entries. 970 sample=sample+1 980 value=RND @ pixel=INT(64+value*384) @ value=value*20-10 990 MOVE 64,position @ LABEL USING "3D,K" ; sample," " 1000 MOVE 511,position @ LABEL USING "S2D.2D,K" ; value," " 1010 MOVE last_pixel,last_position @ DRAW pixel,position 1020 last_position=position @ last_pixel=pixel 1030 NEXT position 1040 ! 1050 IF screen=1 THEN first=0 ELSE first=13 1060 IF screen=num_screens THEN last=254 ELSE last=240 1070 ! 1080 FOR ctr=first TO last ! Dump each row in turn: 1090 MOVE 0,ctr ! ... move to row ... 1100 BREAD line_buffer$,64 ! ... get the row & dump it ... 1110 PRINT USING "#,K" ; "~027*b72W"&pad$&line_buffer$ 1120 NEXT ctr 1130 ! 1140 NEXT screen 1150 ! 1160 PRINT USING "#,K" ; "~027*rB" ! End graphics mode. 1170 PRINT ! Clean out graphics buffer. 1180 ! 1190 END The worst limitation of this approach is that it is S - L - O - W. I checked the length of time the program took to output the entire stripchart - it ran to to 356 seconds, and 90% of that time was strictly printer dumping. This is a definite limitation for high-speed applications. A more sophisticated approach would be to print only twelve dot rows at a time (twelve rows is the minimum that the ThinkJet will print), but even that takes more than a second - and it appears (from some experiments I made) that BASIC cannot do anything else until the ThinkJet has completed printing the row (although I have some tentative ideas on how to work around that). As a result, given that values are to be plotted a centimeter apart, it appears unlikely that samples can be plotted much more often than once every ten seconds. Of course, the sample rate could be doubled by plotting values every half-centimeter. There is, of course, no need to use the entire graphics display to generate the stripchart; a narrow window could be used to display the stripchart one value at a time, while the rest of the display was used for other purposes. (The stripchart window could be selectively cleared by using BREAD's sister function, BPLOT.) A similar approach to that outlined in this note could no doubt be used to drive stripchart data, or other types of graphics, to an external printer like the LaserJet (of course considering the differences in ESCAPE sequences and resolution).