If some developer or development team wants to implement an image acquisition suite on PixInsight, it will be very welcome. And I agree: it would be extremely cool.
If some developer or development team wants to implement an image acquisition suite on PixInsight, it will be very welcome. And I agree: it would be extremely cool.
Right now PixInsight/PCL provides a rich, multiplatform development framework to implement sophisticated graphical interfaces and image processing tools. It is just a matter of putting the device drivers into action (the dirty work); everything else is very easy.
I personally won't start development of anything related to image acquisition. However, any interested developers can count on my support and assistance. PixInsight is an open-architecture system precisely to make everything possible --and it is possible, if someone decides to start a project seriously.
Packing and uploading PI version 1.6.9.651 while I'm writing this, so stay tuned ;)
But it seems like we are still at a point in the architecture doesn't allow modules to communicate with each other. Is this still the case?
The real time monitoring sounds like a challenge to me...
It seems like that might not be possible as a Module - can anyone verify that or not?
This is whole different animal. Testing drivers is full time job and there is always a new one.
Run away... Run away.... :surprised:
Max
...Second, IMO, what's really needed is a multiplatform port of ASCOM. ASCOM is being today, IMO, a bit outdated. There are a lot of multiplatform apps right now, and this will evolve in the near future. So it would be great if we have a platform independent (well... only driver dependent) communitcation protocol for astronomical instruments....
Also, I plan on making the driver architecture dynamic. This means that you will be able to drop drivers into the module - as opposed to the module maintaining a huge list of drivers.
I am planning on supporting ASCOM out of the box
My concern is that it delays other aspects of PI development.
Georg has suggested INDI, which looks like a nice alternative. However, I see it still too immature as to depend on it (please correct me if I'm wrong). I'd keep a close eye on this project though.Indi has not an implementation for Windows, so it is not [yet] multiplatform neither.
Hi David,
Excellent initiative. I like your overall design as a starting point, so go ahead and count on our collaboration with anything related to PI/PCL development!
QuoteI am planning on supporting ASCOM out of the box
I understand your reasons. However, I'd suggest you design your modules with total independence on ASCOM or anything similar. With a proper top-down design and isolation between functional blocks, you can support it without depending on it. ASCOM is Windows-centric, which is something to be avoided for the reasons noted before.
A first step could be answering a simple question: Which camera manufacturers provide drivers for Windows, Linux, and Mac OS X, and what do these drivers have in common? This would give us an idea of what we can expect and where we should be moving on.
ExposeImage
exposureCount
exposureDuration
(cameraConfiguration)
cameraTemperature
filterIndex
isPreviewExposure
binningModeX
binningModeY
fileSavePath
fileNamePrefix
That looks like a very good start. Instead of ExposeImageProcess I'd name it just ExposeImage (the word Process is redundant IMO).
Perhaps even better, AcquireImage (has a more general meaning).
cameraConfiguration requires further elaboration; I don't think it should be part of ExposeImage, it probably should be a global configuration object within your module. I'd add a fileNamePostfix parameter because there are guys out there that prefer postfixing (I know a few :)) Other than these, your design looks very correct to me. Now you need an icon :)This makes sense. I will remove the camera config from the ImageExpose process. There will just be a chooser to choose a camera that is already configured with the ImageAcquisition global preferences. There would be a dialog that pops up telling the user to configure the camera if there are none to choose.
I will definitely keep dithering on the list of TODOs when I attack the guider part...
Cheers,
Dave
Just a preview of the interface so far...it is definitely not a final draft by any means ;-)
I may incorporate a progress bar for the current image being taken.
The only precaution is that all accesses to shared global variables must be protected with Mutex objects.
When do destructors get invoked on the Process classes? Is it only when PixInsight closes? Is there anywhere describing the full lifecycle of the Module / Process?
How can I serialize data from a PixInsight Array<T, A> Class? I'm sure I can do it manually, but I figured there has to be something built in since you are marshaling objects with their instance data...
So let me make sure I am thinking correctly:
When my module is installed, the "InstallPixInsightModule" function is called. This is called everytime PixInsight starts. The function calls the CTORs of my processes contained in the module. Those CTORs all initialize a variable like "TheExposeImageInterface" which is scoped to the module not the class...
Therefore, I can access any of the "The<...>Interface" or "The<...>Process" etc...
Do you refer to descendants of the ProcessImplementation class?
What do you refer by "serializing"? Do you mean persistently storing instances of Array<> in data streams, such as XML documents? Currently, there is no built-in XML support in PCL (this is going to change soon). You can write Array<> to binary files if you want, or you can represent Array<> as plain text. let me know if you are interested in doing this.
IsoString theString = GUI->CamDlg.GetDriverFile().ToIsoString();
char * chars = theString.c_str();
Specifically, I want to store the user's camera settings without requiring them to load a process icon. The Settings API will let me do this, but it doesn't have a convenient way for me to persist my data structures. As a lazy Ruby developer I am spoiled :-P - so yes, I would like to serialize my Array<> to a char* that I can store using the settings API. XML would be wasteful since it is just an internal mechanism. I don't really care if it is human readable. That's what the XPSM files are for ;-)
/*
* Adds an object t to a ByteArray stream b.
*/
template <class T>
void AddToRawData( ByteArray& b, const T& t )
{
const uint8* p = reinterpret_cast<const uint8*>( &t );
b.Add( p, p+sizeof( t ) );
}
/*
* Retrieves an object t from a ByteArray stream at the specified location i.
* Returns an iterator located at the next position in the ByteArray stream.
*/
template <class T>
ByteArray::const_iterator GetFromRawData( T& t, ByteArray::const_iterator i )
{
t = *reinterpret_cast<const T*>( i );
return i + sizeof( T );
}
/*
* Adds the contents of a string s to a ByteArray stream b.
*/
template <class S>
void AddStringToRawData( ByteArray& b, const S& s )
{
AddToRawData( b, uint32( s.Length() ) );
if ( !s.IsEmpty() )
b.Add( reinterpret_cast<const uint8*>( s.Begin() ), reinterpret_cast<const uint8*>( s.End() ) );
}
/*
* Loads a string's character contents from the specified location i on a ByteArray.
* Returns an iterator located at the next position in the ByteArray stream.
*/
template <class S>
ByteArray::const_iterator GetStringFromRawData( S& s, ByteArray::const_iterator i )
{
uint32 n;
i = GetFromRawData( n, i );
if ( n > 0 )
{
s.Assign( reinterpret_cast<const S::char_type*>( i ), 0, n );
i += n * sizeof( S::char_type );
}
else
s.Clear();
return i;
}
/*
* Template instantiations for the String type.
*/
void AddToRawData( ByteArray& b, const String& s )
{
AddStringToRawData( b, s );
}
ByteArray::const_iterator GetFromRawData( String& s, ByteArray::const_iterator i )
{
return GetStringFromRawData( s, i );
}
/*
* Template instantiations for the IsoString type.
*/
void AddToRawData( ByteArray& b, const IsoString& s )
{
AddStringToRawData( b, s );
}
ByteArray::const_iterator GetFromRawData( IsoString& s, ByteArray::const_iterator i )
{
return GetStringFromRawData( s, i );
}
Hope this helps.
UInt32Image PixInsightASCOMDriver::ImageArray()
{
while(!theCameraPtr->ImageReady)
{
Sleep(1000);
}
UInt32Image theImageData;
theImageData.AllocateData(theCameraPtr->NumX, theCameraPtr->NumY);
CComSafeArray< long > safeArr;
safeArr.Attach(theCameraPtr->ImageArray.parray);
long idx[2];
long val;
for(int rowIdx = 0;rowIdx < theCameraPtr->NumY; rowIdx++)
{
UInt32Image::sample* v = theImageData.ScanLine(rowIdx);
for(int colIdx = 0;colIdx < theCameraPtr->NumX; colIdx++)
{
idx[0] = colIdx;
idx[1] = rowIdx;
safeArr.MultiDimGetAt(idx, val);
UInt32PixelTraits::FromSample((pcl::int16&)val, v[colIdx]);
}
}
safeArr.Detach();
return theImageData;
}
UInt32Image PixInsightASCOMDriver::ImageArray()
{
while(!theCameraPtr->ImageReady)
{
Sleep(1000);
}
UInt32Image theImageData;
theImageData.AllocateData(theCameraPtr->NumX, theCameraPtr->NumY);
long HUGEP *pData;
SafeArrayAccessData( theCameraPtr->ImageArray.parray, (void HUGEP* FAR*)&pData );
for(int rowIdx = 0;rowIdx < theCameraPtr->NumY; rowIdx++)
{
UInt32Image::sample* v = theImageData.ScanLine(rowIdx);
memcpy( sample, pData+rowIdx*theCameraPtr->NumX, theCameraPtr->NumX*sizeof(long) );
}
SafeArrayUnaccessData( theCameraPtr->ImageArray.parray );
return theImageData;
}
long HUGEP *pData;
long *pData;
long *pData;
SAFEARRAY *pData;
UInt32PixelTraits::FromSample((pcl::int16&)val, v[colIdx]);
UInt32Image PixInsightASCOMDriver::ImageArray()
{
//TODO: This needs to have some sort of time out.
while(!theCameraPtr->ImageReady)
{
Sleep(1000);
}
long *imageData;
SafeArrayAccessData(theCameraPtr->ImageArray.parray, (void **)&imageData);
int dims = SafeArrayGetDim(theCameraPtr->ImageArray.parray);
long ubound1, ubound2, lbound1, lbound2;
SafeArrayGetUBound(theCameraPtr->ImageArray.parray,1,&ubound1);
SafeArrayGetUBound(theCameraPtr->ImageArray.parray,2,&ubound2);
SafeArrayGetLBound(theCameraPtr->ImageArray.parray,1,&lbound1);
SafeArrayGetLBound(theCameraPtr->ImageArray.parray,2,&lbound2);
int sizeX = ubound1 - lbound1;
int sizeY = ubound2 - lbound2;
UInt32Image theImageData;
theImageData.AllocateData(sizeX, sizeY);
for(int rowIdx = 0;rowIdx < sizeY; rowIdx*=sizeY)
{
UInt32Image::sample* v = theImageData.ScanLine(rowIdx);
memcpy(v, &imageData[rowIdx], sizeX * sizeof(long));
}
SafeArrayUnaccessData( theCameraPtr->ImageArray.parray );
return theImageData;
}
void PixInsightASCOMDriver::ImageArray(UInt32Image &theImage)
{
//TODO: This needs to have some sort of time out.
while(!theCameraPtr->ImageReady)
{
Sleep(1000);
}
long *imageData;
SafeArrayAccessData(theCameraPtr->ImageArray.parray, (void **)&imageData);
int dims = SafeArrayGetDim(theCameraPtr->ImageArray.parray);
long ubound1, ubound2, lbound1, lbound2;
SafeArrayGetUBound(theCameraPtr->ImageArray.parray,1,&ubound1);
SafeArrayGetUBound(theCameraPtr->ImageArray.parray,2,&ubound2);
SafeArrayGetLBound(theCameraPtr->ImageArray.parray,1,&lbound1);
SafeArrayGetLBound(theCameraPtr->ImageArray.parray,2,&lbound2);
int sizeX = ubound1 - lbound1;
int sizeY = ubound2 - lbound2;
theImage.AllocateData(sizeX, sizeY);
for(int rowIdx = 0;rowIdx < sizeY; rowIdx++ )
{
UInt32Image::sample* v = theImage.ScanLine(rowIdx);
for(int colIdx = 0;colIdx < sizeX;colIdx++)
{
//v should be pointing to the current row of data we need to fill
v[colIdx] = UInt32Image::sample(imageData[sizeY*rowIdx + colIdx]);
}
}
SafeArrayUnaccessData( theCameraPtr->ImageArray.parray );
}
imageData[sizeY*rowIdx + colIdx]
uint16 PixInsightASCOMDriver::ASCOMDataToPi( long _i )
{
return _i + 32768;
}
void PixInsightASCOMDriver::ImageArray(UInt16Image &theImage)
{
long *imageData;
SafeArrayAccessData(theCameraPtr->ImageArray.parray, (void **)&imageData);
int dims = SafeArrayGetDim(theCameraPtr->ImageArray.parray);
long ubound1, ubound2, lbound1, lbound2;
SafeArrayGetUBound(theCameraPtr->ImageArray.parray,1,&ubound1);
SafeArrayGetUBound(theCameraPtr->ImageArray.parray,2,&ubound2);
SafeArrayGetLBound(theCameraPtr->ImageArray.parray,1,&lbound1);
SafeArrayGetLBound(theCameraPtr->ImageArray.parray,2,&lbound2);
int sizeX = ubound1 - lbound1;
int sizeY = ubound2 - lbound2;
theImage.AllocateData(sizeX, sizeY);
uint16 *piImageData = *theImage;
for( size_type i = 0, N = theImage.NumberOfPixels(); i < N; ++i)
*piImageData++ = ASCOMDataToPi( *imageData++ );
}
void ImageAcquisitionSettingsInterface::TestImage()
{
activeCamera->SetConnected(true);
activeCamera->StartExposure(1);
Console().WriteLn("taking exposure");
while(!activeCamera->ImageReady())
{
//Console().WriteLn("camera not ready yet...");
}
activeCamera->SetLogger(&theLogger);
UInt16Image img;
activeCamera->ImageArray(img);
ImageWindow newImageWindow(img.Bounds().Height(),img.Bounds().Width());
Image* vImage = newImageWindow.MainView().Image() = img;
newImageWindow.Show();
}
ImageWindow window ( sizeX, // width
sizeY, // height
1, // numberOfChannels
16, // bitsPerSample
false, // floatSample
false, // color
true, // initialProcessing
"camera_test" // id
);
View view = window.MainView();
ImageVariant v = view.Image();
UInt16Image* image = static_cast<UInt16Image*>( v.AnyImage() );
// ...
uint16* piImageData = **image;
for ( size_type i = 0, N = image->NumberOfPixels(); i < N; ++i )
*piImageData++ = ASCOMDataToPI( *imageData++ );
// ...
window.Show();
window.ZoomToFit( false ); // don't allow zoom > 1
Hello David,Thanks
I'm glad you managed the safearray in the end. It's really a big step forward. Two notes:
1) I would be really surprised if ASCOM images are scaled to -32768..32768. They're using long (= int) so the actual range of one value is -2,147,483,648 to 2,147,483,647. The actual range of the provided values should be 0 to Camera.MaxADU (-> property of the camera driver).I was surprised too. The values coming from the driver are negative - but it may just be a bug. Their is definitely more work to do.
2) The image on your screenshot has all pixels in each row equal. If this is not somehow caused by camera, it is likely some bug in your code, causing every row to be filled with only one pixel value. Worth of checking.It is the camera - I compared it to Maxim. It is just a CMOS guider pointed at junk on my desk. It has a line noise reduction routine - which creates a strange BIAS pattern.
long *imageData;
SafeArrayAccessData(theCameraPtr->ImageArray.parray, (void **)&imageData);
Because this is where I am seeing negative values. You mention that they are using long (=int), but I see them using Long - isn't that a 64-bit value?
Here is a reference from MS:
http://msdn.microsoft.com/en-us/library/y595sc15.aspx
void ImageAcquisitionSettingsInterface::__CameraListButtons_Click( Button& sender, bool checked )
{
if(sender == GUI->AddCamera_PushButton)
{
try
{
Console() << "AddCamera clicked\n";
AddCamera();
UpdateCameraList();
} catch( ... )
{
throw Error("Please use unique name for each camera.");
}
}
...
Console() << "foo";
Console c = Console();
c << "foo";
../../ImageAcquisitionSettingsInterface.cpp: In member function ‘void pcl::ImageAcquisitionSettingsInterface::AddCamera()’:
../../ImageAcquisitionSettingsInterface.cpp:181: error: no match for ‘operator<<’ in ‘pcl::Console() << "AddCamera()\012"’
/Users/draphael/PCL/include/pcl/String.h:4692: note: candidates are: pcl::IsoString& pcl::operator<<(pcl::IsoString&, const pcl::GenericString<char, pcl::IsoCharTraits, pcl::StandardAllocator>&)
...
exposeThread = new ExposeImageThread( TheImageAcquisitionSettingsInterface->activeCamera, exposureDuration, exposureCount);
exposeThread->Start( );
while( exposeThread->IsExposing() )
{
//console << "exposing .... \n";
pcl::Sleep(.01);
ProcessInterface::ProcessEvents();
}
return true;
struct ExposeImageData
{
ExposeImageData() :
mutex(), image(), imageProgress( 0 ), imageReady( false ),
abort( false ), error( false ), errorMessage()
{
}
Mutex mutex; // To protect data from concurrent accesses
UInt16Image image; // The image being acquired
int imageProgress; // Progress indicator, e.g. from 0 to 100
bool imageReady; // Flag true if a new image is now ready
bool abort; // Flag true if the user wants to abort
bool error; // Flag true if an error occurs
String errorMessage; // Error information
... // more stuff
};
ExposeImageData data;
class ExposeImageThread : public Thread
{
public:
...
virtual void Run()
{
while ( true )
{
// Check if the user has aborted the process
if ( data.abort )
{
... possibly perform some cleanup here
break;
}
... do your acquisition stuff here
// Check possible errors
if ( error )
{
data.mutex.Lock();
data.error = true;
data.errorMessage = "Hmm, something went wrong...";
data.mutex.Unlock();
break;
}
// Update the progress indicator
data.mutex.Lock();
data.imageProgress++;
data.mutex.Unlock();
// Do we have acquired a new image?
if ( acquisitionComplete )
{
data.mutex.Lock();
data.imageReady = true;
data.imageProgress = 100;
data.mutex.Unlock();
}
// Job done?
if ( finishedAcquiringImages )
break;
}
}
};
class ExposeImageDialog : public Dialog
{
public:
ExposeImageDialog() : Dialog()
{
... build your dialog here
// Handle Abort button click events
Abort_PushButton.OnClick( (Button::click_event_handler)&ExposeImageDialog::__Button_Click, *this );
// Attach your own dialog execution handler, so you gain control
// as soon as your dialog is executed modally.
OnExecute( (Dialog::execute_event_handler)&ExposeImageDialog::__Dialog_Execute, *this );
}
void __Dialog_Execute( Dialog& sender )
{
// Here we are. Now we are the *only* element of PixInsight
// that can be used interactively by the user. Since this has
// been called by the GUI thread, we can do anything we want
// with GUI resources.
// This is your original snippet
exposeThread = new ExposeImageThread( TheImageAcquisitionSettingsInterface->activeCamera, exposureDuration, exposureCount );
exposeThread->Start();
while ( exposeThread->IsExposing() )
{
//console << "exposing .... \n"; <-- ups, forget about this; there is no console available at this point!
pcl::Sleep( .01 );
ProcessInterface::ProcessEvents();
// Now let's see if we have an image ready
data.mutex.Lock();
bool myImageReady = data.imageReady;
data.mutex.Unlock();
if ( myImageReady )
{
... code to create a new image window
}
}
if ( data.error ) // note that the thread is not running at this point
{
MessageBox( data.errorMessage, ... ).Execute();
}
...
}
void __Button_Click( Button& sender, bool checked )
{
// Handle the Abort button
if ( sender == Abort_PushButton )
{
data.mutex.Lock();
data.abort = true;
data.mutex.Unlock();
}
else ... // handle other buttons
}
};
// This is your interface class
class ExposeImageInterface : public ProcessInterface
{
public:
...
private:
void __Button_Click( Button& sender, bool checked )
{
// Handle the Start button
if ( sender == Start_PushButton )
{
ExposeImageDialog dialog;
dialog.Execute();
}
}
};
I was imaging a process container that I could stuff a bunch of different ExposeImage by dragging the triangle to the process container...
class ExposeImageInstance : public ProcessImplementation
{
public:
...
virtual bool CanExecuteGlobal( String& whyNot ) const
{
return true;
}
virtual bool ExecuteGlobal()
{
ExposeImageDialog dialog;
dialog.Execute(); // this is the image acquisition dialog
return !thereAreErrors;
}
...
};
class ExposeImageInterface : public ProcessInterface
{
public:
...
private:
// This is the instance that you are editing on your interface.
// It has the current definitions of cameras, working settings, etc.
ExposeImageInstance instance;
void __Button_Click( Button& sender, bool checked )
{
// Handle the Start button
if ( sender == Start_PushButton )
{
instance.LaunchGlobal(); // execute our instance globally
}
}
};
Personally I like this way, because it adheres much better to the object oriented design in PixInsight. Let me know if this helps you further.
bool ImagingSessionInterface::ValidateProcess( const ProcessImplementation& p, String& whyNot ) const
{
if ( dynamic_cast<const ExposeImageInstance*>( &p ) == 0 &&
dynamic_cast<const AutoFocusInstance*>( &p ) == 0 )
{
whyNot = "Must be an instance of either ExposeImage or AutoFocus.";
return false;
}
whyNot.Clear();
return true;
}
bool ImagingSessionInterface::ImportProcess( const ProcessImplementation& p )
{
const ExposeImageInstance* ei = dynamic_cast<const ExposeImageInstance*>( &p );
if ( ei != 0 )
{
// We are importing an ExposeImage instance
...
}
else
{
const AutoFocusInstance* af = dynamic_cast<const AutoFocusInstance*>( &p );
if ( af != 0 )
{
// We are importing an AutoFocus instance
...
}
else
{
// Hmmm, this is odd; this cannot happen because we have validated
// the instance to ensure it is either ExposeImage or AutoFocus
return false;
}
}
return true;
}
One further question. AutoFocus and ExposeImage are executable processes? In other words, can I execute an instance of ExposeImage without needing an ImagingSession?
If the answer is yes, then why can't you rely on ProcessContainer?I think we can use ProcessContainer...
...you can assign any image to the current image in a view...Would the thread be able to assign that? It seems that would be the case by using the ExposeImageData struct? But I thought that the thread couldn't call anything that updates the UI?
but then we won't have the nice modal dialog reporting all the progress, FWHM monitor etc...?
But I thought that the thread couldn't call anything that updates the UI?
Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 libSystem.B.dylib 0x00007fff836d6616 __kill + 10
1 libSystem.B.dylib 0x00007fff83776cca abort + 83
2 libstdc++.6.dylib 0x00007fff886905d2 __tcf_0 + 0
3 libobjc.A.dylib 0x00007fff81cd4d3d _objc_terminate + 120
4 libstdc++.6.dylib 0x00007fff8868eae1 __cxxabiv1::__terminate(void (*)()) + 11
5 libstdc++.6.dylib 0x00007fff8868eb16 __cxxabiv1::__unexpected(void (*)()) + 0
6 libstdc++.6.dylib 0x00007fff8868ebfc __gxx_exception_cleanup(_Unwind_Reason_Code, _Unwind_Exception*) + 0
7 ...iades-astrophoto.PixInsight 0x0000000100ebf4ec pcl::CriticalSignalHandler(int) + 284
8 libSystem.B.dylib 0x00007fff836e867a _sigtramp + 26
9 ImageAcquisition-pxm.dylib 0x000000012940766e pcl::Thread::IsRootThread() + 14
10 ImageAcquisition-pxm.dylib 0x00000001294066c9 pcl::StatusMonitor::__Reset() + 25
11 ImageAcquisition-pxm.dylib 0x0000000129402336 pcl::AbstractImage::AbstractImage() + 262
12 ImageAcquisition-pxm.dylib 0x0000000129400bbe pcl::Generic2DPixelData<pcl::UInt16PixelTraits>::Generic2DPixelData() + 22 (PixelData.h:192)
13 ImageAcquisition-pxm.dylib 0x0000000129400c68 pcl::Generic2DImage<pcl::UInt16PixelTraits>::Generic2DImage() + 22 (Image.h:257)
14 ImageAcquisition-pxm.dylib 0x0000000129400ce0 pcl::ExposeImageData::ExposeImageData() + 40 (ExposeImageInstance.cpp:9)
...
#include "ExposeImageInstance.h"
namespace pcl
{
struct ExposeImageData
{
ExposeImageData() :
mutex(), image(), imageProgress( 0 ), imageReady( false ),
abort( false ), error( false ), paused( false ), errorMessage()
{
}
Mutex mutex; // To protect data from concurrent accesses
UInt16Image image; // The image being acquired
int imageProgress; // Progress indicator, e.g. from 0 to 100
bool imageReady; // Flag true if a new image is now ready
bool abort; // Flag true if the user wants to abort
bool error; // Flag true if an error occurs
bool paused;
String errorMessage; // Error information
};
ExposeImageData data;
class ExposeImageThread: public Thread
{
...
Note— this should not lead to a crash, but to an error message, so as a bonus you've discovered a bug :)
and initialize it somewhere if it is zero:
if ( data == 0 )
data = new ExposeImageData;
Quoteand initialize it somewhere if it is zero:
if ( data == 0 )
data = new ExposeImageData;
I guess it will get cleaned up when PixInsight exits?
I'd like to periodically update the ExposeImageInterface with the actual sensor temperature of the camera. Is there anyway for me to do this? It is similar to the way an interface can respond to a View event...? The desired use case is to connect the camera, set the temperature and then the interface will display the current temperature for the camera...
#include <pcl/Timer.h>
class ExposeImageInstance : public ProcessImplementation
{
public:
...
private:
...
// The timer used to update camera data at regular intervals
Timer UpdateCameraData_Timer;
...
// Timer event handler
ExposeImageInstance::__UpdateCameraData_Timer( Timer& sender )
};
ExposeImageInstance::GUIData::GUIData( ExposeImageInstance& w )
{
...
UpdateCameraData_Timer.SetInterval( 0.5 );
UpdateCameraData_Timer.SetPeriodic( true );
UpdateCameraData_Timer.OnTimer( (Timer::timer_event_handler)&ExposeImageInstance::__UpdateCameraData_Timer, w );
...
}
// Start generating timer events
GUI->GUI->UpdateCameraData_Timer.Start();
ExposeImageInstance::__UpdateCameraData_Timer( Timer& sender )
{
if ( sender == GUI->UpdateCameraData_Timer )
{
// Will get here every 0.5 seconds
... perform the updates here ...
// You'll have to stop the timer when no more updates are necessary.
if ( no-more-updates-required )
GUI->UpdateCameraData_Timer.Stop();
}
}
QuoteI'd like to periodically update the ExposeImageInterface with the actual sensor temperature of the camera. Is there anyway for me to do this? It is similar to the way an interface can respond to a View event...? The desired use case is to connect the camera, set the temperature and then the interface will display the current temperature for the camera...
Let me know how this works.
Good to know David! How is this project going on? Impatient...
TODOs:
[x] Implement cross platform driver for devices
[x] Camera
[x] Filter Wheel
[ ] Implement Device Simulators
[x] Camera
[/] Filter Wheel (50%)
[x] Unify Current Camera logic across processes (currently there is a direct
access to the pointer...not good)
[ ] Wire up filterWheelData as a globally shared object (similar to cameraData)
[ ] ExposeImage process / instance / interface
[x] Duration
[x] Qty
[x] Camera Connection Logic
[x] Threading for global execution
[ ] Filter selection
[/] Implement FileOutputPattern stuff (50%)
[ ] Fix range sliders. Perhaps use a different UI paradigm.
[x] Implement Temperature Monitoring / UI updates
[ ] Implement Set Temperature
[ ] Implement Frame and Focus
[ ] Implement Sub Frame Exposure
[ ] ImageAcquisitionSettings process / instance / interface
[x] Camera
[ ] Filter Wheel Settings
[/] Implement Serialized Settings Persistance (85%)
[ ] Implement Default Camera Driver ( maybe just in Windows? )
[x] Add thread safety for Camera Data access
[ ] Implement error handling across the different user interactions
[ ] Expose scripting interface (make sure that the std paradigms that PI offers are
adhered to)
#ifndef __SerializableTraits_h
#define __SerializableTraits_h
namespace pcl
{
/*
* Adds an object t to a ByteArray stream b.
*/
template <class T>
inline
void AddToRawData( ByteArray& b, const T& t )
{
const uint8* p = reinterpret_cast<const uint8*>( &t );
b.Add( p, p+sizeof( t ) );
}
/*
* Retrieves an object t from a ByteArray stream at the specified location i.
* Returns an iterator located at the next position in the ByteArray stream.
*/
template <class T>
inline
ByteArray::const_iterator GetFromRawData( T& t, ByteArray::const_iterator i )
{
t = *reinterpret_cast<const T*>( i );
return i + sizeof( T );
}
/*
* Adds the contents of a string s to a ByteArray stream b.
*/
template <class S>
inline
void AddStringToRawData( ByteArray& b, const S& s )
{
AddToRawData( b, uint32( s.Length() ) );
if ( !s.IsEmpty() )
b.Add( reinterpret_cast<const uint8*>( s.Begin() ), reinterpret_cast<const uint8*>( s.End() ) );
}
/*
* Loads a string's character contents from the specified location i on a ByteArray.
* Returns an iterator located at the next position in the ByteArray stream.
*/
template <class S>
inline
ByteArray::const_iterator GetStringFromRawData( S& s, ByteArray::const_iterator i )
{
uint32 n;
i = GetFromRawData( n, i );
if ( n > 0 )
{
s.Assign( reinterpret_cast<const typename S::char_type*>( i ), 0, n );
i += n * sizeof( typename S::char_type );
}
else
s.Clear();
return i;
}
/*
* Template instantiations for the String type.
*/
void AddToRawData( ByteArray& b, const String& s )
{
AddStringToRawData( b, s );
}
ByteArray::const_iterator GetFromRawData( String& s, ByteArray::const_iterator i )
{
return GetStringFromRawData( s, i );
}
/*
* Template instantiations for the IsoString type.
*/
void AddToRawData( ByteArray& b, const IsoString& s )
{
AddStringToRawData( b, s );
}
ByteArray::const_iterator GetFromRawData( IsoString& s, ByteArray::const_iterator i )
{
return GetStringFromRawData( s, i );
}
}
#endif
void CameraItem::AddToRawData( ByteArray& b) const
{
pcl::AddToRawData( b, cameraName );
pcl::AddToRawData( b, driverPath );
// pcl::AddToRawData( b, enabled );
}
ByteArray::const_iterator CameraItem::GetFromRawData( ByteArray::const_iterator i)
{
// return pcl::GetFromRawData( enabled,
return pcl::GetFromRawData( driverPath,
pcl::GetFromRawData( cameraName, i ) ) ;
}
void ImageAcquisitionSettingsInstance::LoadCameras()
{
Console c;
installedCameras.Clear();
ByteArray data;
if( Settings::Read( "CameraData", data ) )
{
CameraItem camera;
for ( ByteArray::const_iterator i = data.Begin(); i < data.End(); i = camera.GetFromRawData( i ) )
{
c << "reading: [ " << camera.cameraName << " ]" << "\n";
installedCameras.Add( camera );
}
}
}
and the save
void ImageAcquisitionSettingsInstance::SaveCameras( )
{
Console c;
ByteArray data;
for ( camera_list::const_iterator i = installedCameras.Begin(); i != installedCameras.End(); ++i )
{
i->AddToRawData( data );
c << "writing: " << i->cameraName << "\n";
}
//Settings::Remove( "CameraData" );
Settings::Write( "CameraData", data );
}
for ( ByteArray::const_iterator i = data.Begin(); i < data.End(); i = camera.GetFromRawData( i ) )
{
c << "reading: [ " << camera.cameraName << " ]" << "\n";
installedCameras.Add( camera );
}
ByteArray::const_iterator i = data.Begin();
while ( i < data.End() )
{
i = camera.GetFromRawData( i );
c << "reading: [ " << camera.cameraName << " ]" << "\n";
installedCameras.Add( camera );
}
for ( ByteArray::const_iterator i = data.Begin(); i < data.End(); )
{
i = camera.GetFromRawData( i );
c << "reading: [ " << camera.cameraName << " ]" << "\n";
installedCameras.Add( camera );
}
static const char *YourProcessNameIcon_XPM[]={
I'm an artistic type (with a computer) and would happy to work on some icons. Can you tell me what sizes you need, which functions they need to match, and any other specs (filetype, transparency, etc)?
As for the name, what about ImageCapture or FrameCapture? Or even CameraControl?
ok, so what can i do to help?
If you get PI to capture Canon cameras, and control an autoguider, you'll be my hero. ;)
Is there a way in PCL SDK to convert a JPEG bytearray into the internal PCL image format?
... Please keep us informed;
For example, the Canon SDK only supports Windows and Mac OS and not Linux
and, even worse, only 32-bit (argh)
...and surprising that they don't have a 64-bit version of their SDK...Yes, hope they will change that soon; I think they have to ...
Do you plan on supporting Nikon cameras too?Currently not, since I don't have a Nikon camera ... developing without testing is not fun ;)
Some updates...
A first version of the DSLR driver for Daves ImageAqcuisition plugin is working now (see attachement). I've tested it a few days ago with my Canon 500D. However currently tested only with the PixInsight Windows 32bit version :-\.
My next steps are:
Finish Filter Wheel Support (This is really just part of Camera Control I think)
Finish Autofocus Support (initially through FocusMax I think)
Image Sessions (Think simple UI to manage everything)
Auto-guiding
Plate-solving
Telescope Control
m = HistogramTransformation::FindMidtonesBalance( .25, m - c0 );
I considered doing some sort of IPC based approach...but I'm already not too impressed with ASCOM - so I'm not sure I'm going to invest any more time with ASCOM. Perhaps someone else will take this part of the project.
I am going to focus in on native drivers on Linux anyways. I don't really like Windows >:D QHY has open sourced a bunch of their Linux stuff...so I will play around with that and see what success I get. If I can't progress with that quickly enough I may revisit the IPC based approach.
Cheers,
Dave
Does anyone know if INDI drivers http://www.indilib.org/ are a viable alternative to ASCOM now?